auto和decltype
有,而且用得很多。C++14 引入了返回类型推导(auto f() { ... }),以前 auto f() -> decltype(expr) 这种尾置返回类型大多可以不写了,所以容易让人觉得 decltype 没用了。实际上,auto 只能"推导一个值的类型",decltype 能"精确查询一个表达式的类型",两者不能互相替代。
根本区别:auto 会退化,decltype 不会
int x = 0; int& rx = x; const int cx = 0;
auto a = rx; // int —— auto 丢掉引用和顶层 const
auto b = cx; // int
decltype // int —— 名字:取声明时的类型
decltype // int&
decltype // const int
decltype // int& —— 加括号变成表达式:左值 → T&
decltype // int —— 纯右值 → T
decltype // int&& —— 将亡值 → T&&
它的规则只有两条:
- 如果是不带括号的名字,结果是这个名字声明时的类型。
- 其他情况按值类别决定:左值得到
T&,将亡值得到T&&,纯右值得到T。
C++14 之后仍然离不开 decltype 的场景
| 场景 | 为什么 auto 不行 | 写法 |
|---|---|---|
| 泛型 lambda 里完美转发 | auto&& 参数没有模板参数名可用 |
[](auto&& x) { f(std::forward<decltype(x)>(x)); } |
| 原样返回引用 | auto 返回类型会退化成值 |
decltype(auto) get(C& c, int i) { return c[i]; },返回 T& |
| 表达式 SFINAE | 推导出的返回类型不参与 SFINAE,函数体里的错误是硬错误 | auto f(T t) -> decltype(t.foo(), void());std::void_t<decltype(...)> 检测惯用法 |
| 拿一个类型去当模板实参或成员类型 | 非静态数据成员不能写 auto,模板实参也不能写 auto |
std::unique_ptr<FILE, decltype(&fclose)>、std::set<int, decltype(cmp)> s(cmp); |
| 没有对象时计算类型 | auto 需要一个真实的初始化表达式 |
decltype(std::declval<T>().size()),std::declval 只能用在这种不求值的上下文里 |
| 实现类型萃取 | 同上 | std::common_type 就是基于 decltype(false ? declval<T>() : declval<U>()) 实现的 |
| 只有声明、定义在别的编译单元 | auto f(); 在定义可见之前不能调用 |
头文件里写 auto f(T) -> decltype(...);,或者直接写出返回类型 |
| 用 lambda 的类型 | lambda 的类型无法写出名字 | C++20 起无状态 lambda 可以默认构造:std::set<int, decltype([](int a, int b) { return a > b; })> |
标准库里的 std::nullptr_t 本身就定义为 decltype(nullptr)。
decltype(auto) 本身就是 decltype
C++14 新增的 decltype(auto) 意思是"用 decltype 的规则来推导",常用来原样转发返回值:
decltype
有一个经典的坑:return (x); 会被推导成 T&,如果 x 是局部变量,就返回了悬空引用。
decltype // 返回 int&,悬空
C++20 之后的变化
- concepts 和
requires取代了大部分 SFINAE 写法:requires { t.foo(); }不再需要decltype(..., void())这种技巧。 - 模板 lambda 可以取代泛型 lambda 里的
decltype(x):[]<class T>(T&& x) { f(std::forward<T>(x)); }。
但以下几个场景仍然需要 decltype:
- 查询表达式的精确类型
decltype(auto)- 拿 lambda 或函数指针的类型去做模板实参
- 写类型萃取
面试时可以这样回答:C++14 的返回类型推导只取代了"尾置 -> decltype(expr) 用来写返回类型"这一种用法。decltype 的本职是不求值地、保留引用和 cv 限定地查询表达式类型,这是 auto 做不到的。
暂无评论,欢迎留下第一条评论。