理解 decltype() 指定符(C++ 11 及以上版本)

2024-08-29 18:28

本文主要是介绍理解 decltype() 指定符(C++ 11 及以上版本),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.  功能

2.  语法格式

3.  理解

3.1 第一点

1.2  第二点

4.  例释


     在 C++ 编程语言中,decltype 是一个用于检查实体的声明类型或表达式的类型和值类别的关键字。该关键字在 C++11 中引入,主要用于泛型编程中,因为在泛型编程中,表达依赖于模板参数的类型通常很困难,甚至不可能。

    随着泛型编程技术在整个 20 世纪 90 年代变得越来越流行,人们认识到了类型推断机制的必要性。许多编译器供应商都实现了自己的运算符版本,通常称为 typeof,并且基于现有语言功能开发了一些功能有限的可移植实现。2002 年,Bjarne Stroustrup 提议在 C++ 语言中添加该运算符的标准化版本,并建议将其命名为“decltype”,以反映该运算符将产生表达式的“声明类型”。

    decltype 的语义(注:指在编程中的实际含义)旨在满足通用库编写者和新手程序员的需求。通常,推断出的类型与源代码中声明的对象或函数的类型完全匹配。与 sizeof运算符一样,decltype 的操作数不会被求值。

1.  功能

检查实体的声明类型或表达式的类型和值类别。

2.  语法格式

    decltype(实体(entity)) decltype(表达式(expression)):这里的实体可以是类对象,也可以是基本内置数据类型变量。

3.  理解

3.1 第一点

如果参数是未加括号的id表达式或未加括号的类成员访问表达式,则 decltype 会得出此表达式所命名实体的类型。如果不存在这样的实体,或者参数命名了一组重载函数,则程序格式不正确(C++ 11 版本)。

如果参数是一个未带括号的 id 表达式,用于命名结构化绑定,则 decltype 会产生引用类型(在结构化绑定声明的规范中描述)(C++ 17 及以上版本)。

如果参数是未带括号的 id 表达式,用于命名非类型模板参数,则 decltype 会生成模板参数的类型(如果模板参数是用占位符类型声明的,则在执行任何必要的类型推断之后)。即使实体是模板参数对象(即 const 对象),该类型也是非常量(C++ 20及以上版本)。

1.2  第二点

如果参数是任何其他类型 T 的表达式,且

(1) 如果表达式的值类别是 xvalue(一个“eXpiring”值,即“将逝值”,表示其资源可以复用的对象,例如,类对象),则 decltype 产生 T&&;

(2) 如果表达式的值类别是左值(lvalue,即可通取内存地址访问的对象),则 decltype 产生 T&;

(3) 如果表达式的值类别是 prvalue(“pure”rvalue,非左值),则 decltype 产生 T。

    如果表达式是一个返回类类型的 prvalue 的函数调用,或者是右操作数是这样的函数调用的逗号表达式,则不会为该 prvalue 引入临时对象(C++17及以下版本)。

    若表达式是除(可能带括号的)立即调用(自 C++20 起)之外的 prvalue,则不会从该 prvalue 中实现临时对象:这样的 prvalue 没有结果对象(C++17及以上版本)。

    由于没有创建临时对象,因此类型不需要完整或具有可用的析构函数,并且可以是抽象的。此规则不适用于子表达式:在 decltype(f(g())) 中,g() 必须具有完整类型,但 f() 不需要。

    请注意,如果对象的名称被括号括起来,它将被视为普通的左值表达式,因此 decltype(x) 和 decltype((x)) 通常是不同的类型。

    decltype 在声明难以或无法使用标准符号声明的类型时很有用,例如与 lambda 相关的类型或依赖于模板参数的类型。

4.  例释

#include <cassert>

#include <iostream>

#include <type_traits>

 

struct A { double x; };

const A* a;

 

decltype(a->x) y;       // y的类型double (声明类型)

decltype((a->x)) z = y; // z的类型是const double& (lvalue 表达式)

 

template<typename T, typename U>

auto add(T t, U u) -> decltype(t + u) // 返回类型取决于模板参数

                                      // C++14之后的版本返回类型可推导

{

    return t + u;

}

 

const int& getRef(const int* p) { return *p; }

static_assert(std::is_same_v<decltype(getRef), const int&(const int*)>);

auto getRefFwdBad(const int* p) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdBad), int(const int*)>,

    "Just returning auto isn't perfect forwarding.");

decltype(auto) getRefFwdGood(const int* p) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdGood), const int&(const int*)>,

    "Returning decltype(auto) perfectly forwards the return type.");

 

// Alternatively:

auto getRefFwdGood1(const int* p) -> decltype(getRef(p)) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdGood1), const int&(const int*)>,

    "Returning decltype(return expression) also perfectly forwards the return type.");

 

int main()

{

    int i = 33;

    decltype(i) j = i * 2;

    static_assert(std::is_same_v<decltype(i), decltype(j)>);

    assert(i == 33 && 66 == j);

 

    auto f = [i](int av, int bv) -> int { return av * bv + i; };

    auto h = [i](int av, int bv) -> int { return av * bv + i; };

    static_assert(!std::is_same_v<decltype(f), decltype(h)>,

        "The type of a lambda function is unique and unnamed");

 

    decltype(f) g = f;

    std::cout << f(3, 3) << ' ' << g(3, 3) << '\n';

}

输出:42 42

这篇关于理解 decltype() 指定符(C++ 11 及以上版本)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1118609

相关文章

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

C++ 中的 if-constexpr语法和作用

《C++中的if-constexpr语法和作用》if-constexpr语法是C++17引入的新语法特性,也被称为常量if表达式或静态if(staticif),:本文主要介绍C++中的if-c... 目录1 if-constexpr 语法1.1 基本语法1.2 扩展说明1.2.1 条件表达式1.2.2 fa

使用Python实现获取网页指定内容

《使用Python实现获取网页指定内容》在当今互联网时代,网页数据抓取是一项非常重要的技能,本文将带你从零开始学习如何使用Python获取网页中的指定内容,希望对大家有所帮助... 目录引言1. 网页抓取的基本概念2. python中的网页抓取库3. 安装必要的库4. 发送HTTP请求并获取网页内容5. 解

IDEA中Git版本回退的两种实现方案

《IDEA中Git版本回退的两种实现方案》作为开发者,代码版本回退是日常高频操作,IntelliJIDEA集成了强大的Git工具链,但面对reset和revert两种核心回退方案,许多开发者仍存在选择... 目录一、版本回退前置知识二、Reset方案:整体改写历史1、IDEA图形化操作(推荐)1.1、查看提

C++中::SHCreateDirectoryEx函数使用方法

《C++中::SHCreateDirectoryEx函数使用方法》::SHCreateDirectoryEx用于创建多级目录,类似于mkdir-p命令,本文主要介绍了C++中::SHCreateDir... 目录1. 函数原型与依赖项2. 基本使用示例示例 1:创建单层目录示例 2:创建多级目录3. 关键注

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

C++字符串提取和分割的多种方法

《C++字符串提取和分割的多种方法》在C++编程中,字符串处理是一个常见的任务,尤其是在需要从字符串中提取特定数据时,本文将详细探讨如何使用C++标准库中的工具来提取和分割字符串,并分析不同方法的适用... 目录1. 字符串提取的基本方法1.1 使用 std::istringstream 和 >> 操作符示

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.