理解 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

相关文章

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

浅谈配置MMCV环境,解决报错,版本不匹配问题

《浅谈配置MMCV环境,解决报错,版本不匹配问题》:本文主要介绍浅谈配置MMCV环境,解决报错,版本不匹配问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录配置MMCV环境,解决报错,版本不匹配错误示例正确示例总结配置MMCV环境,解决报错,版本不匹配在col