【C++】深入理解decltype和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()
{
	// 情况1
	int 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;

	// 情况2
	cout << "decltype(func(1)) is int ? " << std::boolalpha << std::is_same<decltype(func(1)), int>::value << endl;

	// 情况3
	int 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()
{
	// 情况3
	int 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;  // 输出 100

    return 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);
}

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

相关推荐

  1. 110 C++ decltype含义,decltype 主要用途

    2024-06-09 14:50:07       24 阅读
  2. 深入理解c++ 继承

    2024-06-09 14:50:07       28 阅读
  3. 深入理解运用C语言中的Break语句

    2024-06-09 14:50:07       37 阅读
  4. 深入理解 C# Unity 中的事件委托

    2024-06-09 14:50:07       14 阅读
  5. 深入理解 C#Unity中的Null

    2024-06-09 14:50:07       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-09 14:50:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-09 14:50:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-09 14:50:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-09 14:50:07       18 阅读

热门阅读

  1. 谷神后端代码模板:导入

    2024-06-09 14:50:07       10 阅读
  2. Docker:认识Docker镜像

    2024-06-09 14:50:07       8 阅读
  3. elmentUI el-table 总结行

    2024-06-09 14:50:07       9 阅读
  4. MySQL:MySQL的EXPLAIN各字段含义详解

    2024-06-09 14:50:07       10 阅读
  5. 状态设计模式完成商品的创建状态之间的流转

    2024-06-09 14:50:07       11 阅读
  6. Rust-08-枚举和模式匹配

    2024-06-09 14:50:07       8 阅读