C++ decay/decltype/declval
一、decay
在 C++ 14 后,可使用别名
std::decay_t在 C++ 中,Decay(退化)是指将一种类型转换为另一种更简单、更“原始”的类型的过程。这通常发生在将表达式传递给函数参数或进行模板推导时。
在 C++11 中,标准库提供了 std::decay(定义在 `` 头文件中) ,它精确地实现了上述规则。它是模板元编程中非常重要的工具。
Decay-copy(“衰变复制”或“退化复制”)是 C++ 标准库中一个非常重要的概念,它通常在涉及泛型编程和模板时发挥作用,尤其是在处理右值引用和转发引用时。
这个概念的核心在于 std::decay 类型特性和完美转发的应用。
std::decay<T>::type 的作用是模拟函数按值传递时参数类型的“衰变”(类型转换)。
- 将数组类型(如
int[5])衰变成指向其元素类型的指针类型(如int*) - 去除顶层的
const和volatile修饰符(cv 限定符) - 将函数类型(如
void(int))衰变成函数指针类型(如void(*)(int)) - 去除引用(
&、&&)
这套转换规则正是 C++ 函数按值传参时编译器自动执行的类型调整。std::decay把它抽象成一个模板元函数,让我们能在编译期获取“退化后”的类型。
Decay-copy 本质上是使用 std::decay 处理后的类型进行复制。在 C++11/14/17 中,标准库的一些组件(如 std::bind、std::thread::thread 构造函数)在内部会使用 std::decay_t(C++14/17 别名)或类似机制来保存(复制)它们所捕获或接收的参数。
实例:
1 | |
1 | |
二、decltype
三、declval
在模板元编程中,std::declval 是一个“虚构对象”,它定义在 <utility> 头文件中。
它的核心作用是:在编译期,欺骗编译器,让它认为你已经拥有了一个类型为 T 的对象。用于在不实际调用构造函数的情况下生成一个对象引用,以便在类型推导中使用。通常在需要引用某个类型的对象但实际无法创建该对象的上下文中使用,例如在函数返回类型推导中:
1 | |
在模板中,如果你想获取某个表达式(例如调用某个成员函数)后的返回类型,你通常需要写出: decltype(obj.method())
但如果 obj 的类型 T:
- 没有默认构造函数(必须传递参数才能创建)
- 构造函数极其复杂(你想在编译期检查,而不是运行期构造)
- 构造函数是私有的
这时候你无法直接创建 T 的实例,std::declval 完美解决了这个问题。
在这个例子中,std::declval().process() 用于推导函数的返回类型,但并不会实际调用 process 函数。
实例:推导一个复杂的返回类型
假设我们有一个类 Widget,它没有默认构造函数:
1 | |
注意:
- 绝对不要在运行时调用:
std::declval()仅存在于编译器眼中。如果在你的普通函数体内调用它,会导致编译链接失败 - 左值 vs 右值:
std::declval():返回T&&(右值)。std::declval():返回T&(左值)。- 这对于重载函数的分辨非常重要(例如判断一个类是否支持
push_back(T&&)和push_back(const T&))。