一、decltype 是什么?
decltype 是 C++11 引入的类型推导关键字。
它的作用是:根据一个表达式,推导出该表达式对应的类型,并将其作为变量、函数返回值或其他声明的类型。
基本语法:
decltype(expression) variable;
例如:
cpp
int a = 10;
decltype(a) b = 20;
此时 decltype(a) 推导出 int,因此相当于:
int b = 20;
注意:decltype 只推导类型,不会执行表达式。
这里可能会感觉大费周章只是decltype(a)替代了int
二、为什么需要 decltype?
核心原因:有时候我们希望一个变量的类型与另一个表达式的类型保持一致,但又不想手动指定类型。
1. 避免重复书写复杂类型
例如:
cpp
#include <iostream>
#include <vector>
using std::string;
std::vector<string> vec;
int main() {
decltype(vec) vec1;
decltype(vec) vec2;
decltype(vec) vec3;
return 0;
}
vec1,vec2,vec3自动获得与 vec 相同的类型,不然vec1,vec2,vec3每个都要写一堆前缀,也挺麻烦的。
如果以后修改 vec 的类型,vec1,vec2,vec3不需要跟着手动修改。
2. 获取表达式真正的返回类型
例如:
int a = 10;
double b = 3.14;
decltype(a + b) result = a + b;
a + b 的结果类型是 double,因此 result 是 double。
3. 配合模板编程
这是 decltype 非常重要的用途。
template<typename T1, typename T2>
auto add(T1 a, T2 b) -> decltype(a + b) {
return a + b;
}
这里:
-
T1、T2是两个参数的类型。 -
decltype(a + b)推导加法表达式的结果类型。 -
auto表示函数返回类型采用后置返回类型声明。
例如:
auto result = add(10, 3.14);
返回类型是 double。
为什么需要它?
因为模板参数的类型可能不同,无法提前确定运算结果类型。decltype 可以根据实际表达式推导结果类型。
三、decltype 最重要的推导规则
这是学习 decltype 必须掌握的部分。
它不是简单地推导表达式的值类型,而是会考虑表达式的值类别。
主要分为三种情况:
| 表达式类型 | decltype 推导结果 |
|---|---|
| 普通变量名(不加括号) | 该变量声明时的类型 |
| 左值表达式 | T& |
| 将亡值表达式 | T&& |
| 纯右值表达式 | T |
我们逐个理解。
1. 普通变量名:推导变量声明时的类型
int a = 10;
decltype(a) x = 20;
x 的类型是 int。
特别注意:
cpp
int a = 10;
decltype(a) x = 20;
这里:
decltype(a)是int。
当表达式是未加括号的标识符表达式时,直接使用该标识符所声明的类型。
2. 左值表达式:推导为 T&
int a = 10;
decltype((a)) b = a;
推导结果:
int& b = a;
因为 (a) 是左值表达式。
3. 将亡值表达式:推导为 T&&
int a = 10;
decltype(std::move(a)) b = std::move(a);
推导结果:
int&& b = std::move(a);
因为 std::move(a) 是将亡值表达式(xvalue)。
4. 纯右值表达式:推导为 T
decltype(10) a = 10;
推导结果:
int a = 10;
因为字面量 10 是纯右值(prvalue)。
四、几个特别容易踩坑的地方
注意事项 1:decltype(a) 和 decltype((a)) 不一样,这个在上面的例子也已经说明,这里虽然有点重复,但是也再次说明一下吧
int a = 10;
decltype(a) x = 20; // int
decltype((a)) y = a; // int&
记忆方法:
-
不加括号的变量名:取变量声明类型。
-
加括号后:按照表达式的值类别推导。
注意事项 2:const 和引用属性会保留
const int a = 10;
decltype(a) b = 20;
此时:
const int b = 20;
因为 decltype(a) 会保留原变量的 const 属性。
再看:
int x = 10;
int& ref = x;
decltype(ref) another = x;
another 仍然是 int&,而不是 int。
注意事项 3:decltype 不会执行表达式
int a = 10;
decltype(a + 100) b = 20;
这里不会真的执行 a + 100 来获取结果。
编译器只根据表达式规则推导类型。
注意事项 4:decltype(auto) 和 auto 不一样
C++14 引入了 decltype(auto)。
例如:
int a = 10;
auto x = a;
decltype(auto) y = (a);
结果:
int x = 10;
int& y = a;
原因:
-
auto通常会进行类型推导,并去掉顶层const和引用。 -
decltype(auto)则完全采用decltype的推导规则。
因此,decltype(auto) 可以保留引用和 const 属性,但也容易意外返回引用。
五、实际开发中什么时候使用 decltype?
| 使用场景 | 目的 |
|---|---|
| 复杂变量类型 | 避免重复书写类型 |
| 模板编程 | 根据表达式推导结果类型 |
| 函数返回值 | 精确指定返回类型 |
| 泛型代码 | 保留表达式的引用属性 |
decltype(auto) |
精确保留类型、引用和 const 属性 |
六、最后总结:记住这几个核心知识点
- decltype 的作用
根据表达式推导类型,不执行表达式。
-
最重要的推导规则
int a = 10;
decltype(a) x = 10; // int
decltype((a)) y = a; // int&
decltype(10) z = 10; // int -
三种值类别
-
左值(lvalue):
T& -
将亡值(xvalue):
T&& -
纯右值(prvalue):
T
- decltype(auto)
完全按照 decltype 规则推导,能够保留引用和 const 属性。
decltype 不只是推导一个表达式的类型,还能通过表达式的值类别,准确保留左值引用、右值引用和 const 属性。
这也是它在 C++ 模板编程和泛型编程中非常重要的原因,而不是像文章开头那里感觉大费周章只替换了int