当前位置: 首页 > news >正文

【C++】深入理解decltype和decltype(auto)

深入理解decltype和decltype(auto)

  • 一、decltype语法介绍
  • 二、decltype的推导规则
    • 1. expr不加括号
    • 2. expr加上括号
  • 三、关于decltype的CV属性推导
  • 四、 decltype(auto) 的使用

一、decltype语法介绍

decltype关键字是C++11新标准引入的关键字,它和关键字auto的功能类似,也可以自动推导出给定表达式的类型,但它和auto的语法有些不同,auto推导的表达式放在=的右边,并作为auto所定义的变量的初始值,而decltype是和表达式结合在一起,语法如下:

decltype(expr) var;

它的语法像是函数调用,但它不是函数调用而是运算符,和sizeof运算符类似,在编译期间获取他的类型,表达式expr不会被真正执行,因此不会产生汇编代码。

decltypeauto在功能上大部分相似,但推导规则和应用场景存在一些区别:

  • auto定义变量时必须提供初始值表达式,利用初始值表达式推导出类型并用它作为变量的初始值,使用auto作为值语义的推导时,会忽略表达式expr的引用性CV属性
  • decltype定义变量时可以不需要初始值。还有decltype可以保留引用性CV属性
  • 引用性:表达式的引用属性,如左值引用或者右值引用。
  • CV 属性:指的是 const 和 volatile 修饰符。它们用于修饰类型,以指定对象的特殊属性和行为。

二、decltype的推导规则

decltype的推导规则主要有三条规则:

  • 如果 expr 一个类成员访问表达式,或者是一个单独的变量,decltype(expr) 的类型就和 expr 一致,这是最普遍最常见的情况。
  • 如果 expr 是函数调用,那么 decltype(expr) 的类型就和函数返回值的类型一致。
  • 如果 expr 是一个左值,或者被括号()包围,那么 decltype(expr) 的类型就是 expr 的引用;假设 expr 的类型为 T,么 decltype(expr) 的类型就是 T&

为了更好地理解 decltype 的推导规则,下面来看几个实际的例子。

1. expr不加括号

#include <iostream>
#include <type_traits>int func(int x) {return x;
}class Base {
public:int x = 0;
};int main()
{// 情况1int i = 1;const int& j = i;decltype(j) x = j;	decltype(i) y = i;// is_same是C++11之后的一个类模板,用于判断两个类型是否一致cout << "x is const int& ? " << std::boolalpha << std::is_same<decltype(x), const int&>::value << endl;cout << "y is int ? " << std::boolalpha <<std::is_same<decltype(y), int>::value << endl;const Base b;cout << "decltype(b.x) is int ? " << std::boolalpha << std::is_same<decltype(b.x), int>::value << endl;int  x1 = 1, x2 = 2;cout << "decltype(x1 + x2) is int ? " << std::boolalpha << std::is_same<decltype(x1 + x2), int>::value << endl;cout << "decltype(x1, x2) is int ? " << std::boolalpha <<std::is_same<decltype(x1, x2), int>::value << endl;cout << "decltype(x1, 0) is int ? " << std::boolalpha << std::is_same<decltype(x1, 0), int>::value << endl;// 情况2cout << "decltype(func(1)) is int ? " << std::boolalpha << std::is_same<decltype(func(1)), int>::value << endl;// 情况3int a[10] = { 0 };cout << "decltype(a[1] is int& ? " << std::boolalpha << std::is_same<decltype(a[1]), int&>::value << endl;return 0;
}

在这里插入图片描述

2. expr加上括号

int main()
{// 情况3int x1 = 1;int x2 = 2;const Base b;cout << "decltype((x1 + x2)) is int ? " << std::boolalpha << std::is_same<decltype((x1 + x2)), int>::value << endl;cout << "decltype((x1)) is int& ? " << std::boolalpha << std::is_same<decltype((x1)), int&>::value << endl;cout << "decltype((b.x)) is const int& ? " << std::boolalpha << std::is_same<decltype((b.x)), const int&>::value << endl;return 0;
}

在这里插入图片描述

(1)式中相加后的结果是一个右值,加上括号后依然是一个右值,因此推导结果是int

(2)式中跟之前没有加括号的情况不一样,加上括号相当于是返回x1变量,因此是一个左值,推导结果是一个引用。

(3)式返回的是一个左值,推导结果是一个引用,但因为定义的类对象b是一个const对象,要保持它的内容不可被修改,因此引用要加上const修饰。

三、关于decltype的CV属性推导

  • 通常情况下,decltype(expr)所推导的类型会同步expr的cv限定符
  • expr是未加括号的成员变量时,对象表达式的cv限定符会被忽略
class Base {
public:int x = 0;
};int main()
{// CV限定符的推导const int i = 0;cout << "decltype(i) is const int ? " << std::boolalpha << std::is_same<decltype(i), const int>::value << endl;const Base b;cout << "decltype(b.x) is int ? " << std::boolalpha << std::is_same<decltype(b.x), int>::value << endl;cout << "decltype((b.x)) is const int& ? " << std::boolalpha << std::is_same<decltype((b.x)), const int&>::value << endl;return 0;
}

在这里插入图片描述

为什么decltype((b.x))const int&而不是const int呢?

因为decltype 的第三条推导规则:被括号()包围,会变成引用类型。

写到这里,不得不感概C++的语法是真的难,头都要给我学秃了…… 😭😭😭

四、 decltype(auto) 的使用

decltype(auto) 是 C++14 引入的一种类型推导机制,它结合了 decltypeauto 的特性。

使用 decltype(auto) 表示让auto使用自动类型推导,但推导的规则是按照decltype的规则来推导,这样在推导时会保留表达式的值的引用性和CV属性

decltype(auto)的使用语法规则如下:

decltype(auto) var = expr;
  • 简单使用示例:
int i = 1;
const int& j = i;
decltype(auto) x = j;	// x的类型为const int&
decltype(auto) y = i;	// y的类型为int
  • decltype(auto)用于推导函数返回值的类型
#include <iostream>
#include <vector>std::vector<int> vec = {1, 2, 3, 4, 5};// 返回左值引用
decltype(auto) getElementRef(size_t index) {return vec[index];
}// 返回右值
decltype(auto) getElementValue(size_t index) {return vec[index] + 0;  // 使用右值表达式
}int main() {// 使用 getElementRef 返回左值引用decltype(auto) ref = getElementRef(2);ref = 100;  // 修改引用的值std::cout << "vec[2]: " << vec[2] << std::endl;  // 输出 100// 使用 getElementValue 返回右值decltype(auto) val = getElementValue(2);std::cout << "val: " << val << std::endl;  // 输出 100return 0;
}
  • decltype(auto)使用陷阱

最后,对于decltype(auto)能够推导函数返回值为引用类型这一点,需要提醒一下的是,小心会有下面的陷阱,如下面的函数:

decltype(auto) func() {int x;// do something...return x;
}

这里推导出来的返回值类型是int,并且会拷贝局部变量x的值,这个没有问题。但如果是这样的定义:

decltype(auto) func() {int x;// do something...return (x);
}

这个版本返回的是一个引用,它将引用到一个即将销毁的局部变量上,当这个函数返回后,所返回的引用将引用到一个不存在的变量上,造成引用空悬的问题,程序的结果将是未知的。无论是有意的还是无意的返回一个引用,都要特别小心。

相关文章:

  • MyBatisPlus插件生成代码
  • Web前端 CodeView:深度解析与实用指南
  • .net后端程序发布到nignx上,通过nginx访问
  • 【React】json-server
  • 【第13章】SpringBoot实战篇之项目部署
  • 医疗器械网络安全风险管理的基本步骤
  • 多关键字排序
  • C++STL(四)priority_queue的详细用法及仿函数实现
  • 什么是pump?pump跟单机器人是什么?
  • Windows Docker手动迁移镜像
  • 深入理解交叉熵损失CrossEntropyLoss - 信息论(交叉熵)
  • JVM学习-监控工具(三)
  • 如何从 Android 图库中恢复误删除的照片
  • 鸿蒙认证学什么?
  • Nagios的安装和使用
  • 分享一款快速APP功能测试工具
  • .pyc 想到的一些问题
  • 【Linux系统编程】快速查找errno错误码信息
  • 2018一半小结一波
  • Android 控件背景颜色处理
  • canvas实际项目操作,包含:线条,圆形,扇形,图片绘制,图片圆角遮罩,矩形,弧形文字...
  • css的样式优先级
  • ECS应用管理最佳实践
  • Java基本数据类型之Number
  • Python socket服务器端、客户端传送信息
  • Quartz初级教程
  • Solarized Scheme
  • TCP拥塞控制
  • Vue 重置组件到初始状态
  • 彻底搞懂浏览器Event-loop
  • 分享几个不错的工具
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 简单数学运算程序(不定期更新)
  • 聊聊hikari连接池的leakDetectionThreshold
  • 前端存储 - localStorage
  • 如何编写一个可升级的智能合约
  • 小程序button引导用户授权
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • kubernetes资源对象--ingress
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • #QT(TCP网络编程-服务端)
  • (09)Hive——CTE 公共表达式
  • (1)(1.11) SiK Radio v2(一)
  • (1)(1.13) SiK无线电高级配置(六)
  • (11)MATLAB PCA+SVM 人脸识别
  • (16)Reactor的测试——响应式Spring的道法术器
  • (23)Linux的软硬连接
  • (Note)C++中的继承方式
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (四) 虚拟摄像头vivi体验
  • (一)RocketMQ初步认识
  • (原創) 如何讓IE7按第二次Ctrl + Tab時,回到原來的索引標籤? (Web) (IE) (OS) (Windows)...
  • (转)四层和七层负载均衡的区别
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]