概述
decltype从C++11引入,用来在编译期获取一个实体或表达式的类型- 它与
auto最大的区别是:auto通常会进行类型退化,而decltype会精确保留const、引用、数组以及表达式的值类别
规则一:未加括号的名字或成员访问
- 如果操作数是:
- 未加括号的变量名
- 未加括号的类成员访问
- 那么结果是该实体声明时的类型
|
1 2 3 |
decltype(variable) decltype(object.member) decltype(pointer->member) |
|
1 2 3 4 5 6 7 8 9 10 |
int x = 10; const int cx = 20; int& lref = x; int&& rref = 30; decltype(x) // int decltype(cx) // const int decltype(lref) // int& decltype(rref) // int&& |
- 尤其注意:
|
1 2 3 4 5 6 |
int&& rref = 30; // 虽然 rref 的声明类型是 int&&,但名字 rref 出现在表达式中时是左值 // 因此: decltype(rref) // int&&:规则一,看声明类型 decltype((rref)) // int& :规则二,rref 表达式是左值 |
规则二:其他所有表达式
| 表达式值类别 | decltype(expr) |
lvalue 左值 |
T& |
xvalue 将亡值 |
T&& |
prvalue 纯右值 |
T |
括号为什么会改变结果
- 括号不会改变
x的值类别,但会使它避开规则一,进入表达式规则
|
1 2 3 4 5 |
int x = 10; // 下面两者完全不同 decltype(x) // int decltype((x)) // int& |
根据值类别推导
左值产生 T&
- 常见左值表达式包括
- 变量名
- 解引用
- 下标访问
- 前置自增
- 返回左值引用的函数
|
1 2 3 4 5 6 |
int x = 10; int* p = &x; decltype((x)) // int& decltype(*p) // int& decltype(++x) // int& |
- 数组下标通常也是左值
- 注意,
array[0]不是简单变量名,因此直接使用表达式规则,不需要额外加括号
- 注意,
|
1 2 3 |
int array[3]{}; decltype(array[0]) // int& |
将亡值产生 T&&
|
1 2 3 4 5 |
#include <utility> int x = 10; decltype(std::move(x)) // int&& |
std::move(x)本身不执行移动,只把x转换为将亡值,所以:
|
1 |
xvalue → T&& |
- 返回右值引用的函数调用也是将亡值
|
1 2 3 |
int&& transfer(); decltype(transfer()) // int&& |
纯右值产生 T
|
1 2 3 4 5 6 |
int x = 10; decltype(42) // int decltype(x + 1) // int decltype(x++) // int decltype(true) // bool |
- 返回值类型为
T的函数调用通常是纯右值
|
1 2 3 |
int make_value(); decltype(make_value()) // int |
函数返回值的推导
- 假设
|
1 2 3 |
int make_value(); int& get_reference(); int&& get_rvalue_reference(); |
- 那么
|
1 2 3 |
decltype(make_value()) // int decltype(get_reference()) // int& decltype(get_rvalue_reference())// int&& |
成员访问的特殊规则
- 未加括号:看成员的声明类型
|
1 2 3 4 5 6 7 |
struct Session { int socket; }; Session session{}; const Session const_session{}; |
|
1 2 |
decltype(session.socket) // int decltype(const_session.socket) // int |
- 加括号:看表达式值类别
|
1 2 |
decltype((session.socket)) // int& decltype((const_session.socket)) // const int& |
数组和函数不会自动退化
数组
|
1 2 3 4 |
int buffer[1024]; decltype(buffer) // int[1024] decltype((buffer)) // int (&)[1024] |
字符串字面量
|
1 |
decltype("hello") // const char (&)[6] |
函数
- 第一个是函数类型,第二个是函数左值引用
|
1 2 3 4 |
int process(int); decltype(process) // int(int) decltype((process)) // int (&)(int) |
条件表达式的推导
- 条件表达式的值类别取决于第二、第三个操作数
|
1 |
b < a ? a : b |
相同类型的两个左值
- 因为
a、b都是相同类型的左值,条件表达式结果也是左值
|
1 2 3 4 |
int a = 10; int b = 20; decltype(true ? a : b) // int& |
|
1 |
左值 → int& |
- 下面代码的危险所在
|
1 2 3 4 5 6 |
template<typename T> auto bad_max(T a, T b) -> decltype(b < a ? a : b) { return b < a ? a : b; } |
|
1 2 |
// 当 T=int 时,返回类型是: int& // 但 a、b 是局部变量,函数结束后被销毁,于是返回悬空引用 |
decltype 不会执行表达式
decltype的操作数属于不求值操作数
|
1 2 3 |
int x = 10; using Result = decltype(++x); |
|
1 2 3 4 5 6 7 |
Result // int& x // 仍然是 10 // ++x 没有真的执行 // 同理: decltype(some_function()) // 不会调用 some_function() |
decltype(auto)
C++14引入decltype(auto),它会对初始化表达式使用完整的decltype规则
|
1 2 3 4 5 6 7 8 9 10 |
int x = 10; decltype(auto) a = x; // int decltype(auto) b = (x); // int& decltype(auto) a = x; // 相当于 decltype(x) → int decltype(auto) b = (x); // 相当于 decltype((x)) → int& |
decltype(auto) 返回值陷阱
case
|
1 2 3 4 5 |
decltype(auto) bad() { int value = 42; return (value); } |
- 推导过程
|
1 |
decltype((value)) // int& |
|
1 2 3 |
// 所以函数实际返回: int& bad(); |
|
1 2 3 4 5 6 7 8 9 10 |
// 如果去掉括号: decltype(auto) by_value() { int value = 42; return value; } // 由于 value 是未加括号的名字 decltype(value) // int |
返回成员时的微妙差别
case
|
1 2 3 4 5 6 7 8 9 10 11 |
#include <string> struct Session { std::string name; }; decltype(auto) get_name_copy(Session& session) { return session.name; } |
- 返回表达式是未加括号的成员访问
- 所以函数按值返回,会复制字符串
|
1 |
decltype(session.name) // std::string |
- 加上括号:
|
1 2 3 4 |
decltype(auto) get_name_ref(Session& session) { return (session.name); } |
|
1 |
decltype((session.name)) // std::string& |
auto推导规则
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ C++并发编程 _管理线程05/07
- ♥ gflags记述:记录210/09
- ♥ C++_多态、类型转换、数据段、BSS段、类型视图06/21
- ♥ C++17_第一篇12/20
- ♥ Boost程序库完全开发指南:时间与内存08/21
- ♥ C++_关于Shared_ptr管理内存05/30