C++ 11引入了auto/decltype关键字来进行类型推导,其在一些场景中可以提高代码简洁性和可读性。 auto关键字的类型推导规则基本上和模板参数类型推导规则一致,Effective Modern C++ Chapter 1对此有很详细的讨论,本文的主要参考内容也是该书。 decltype可以原汁原味地返回变量名对应的变量类型,其可以完成auto关键字不能胜任的一些corner case任务。 本文接下来将以较短的篇幅对这三者进行介绍,希望能对大家有所帮助。
1. 模板参数类型推导
对于函数模板 template<typename T> f(T&|T*|T&&|T)
,其模板参数类型的可选类型包括T&|T*|T&&|T.
若f(ParamType)为f(T|…)完成类型推导后的结果,那么ParamType则为类型推导完成后的实际函数入参类型。
当函数实参为相关int类型时,类型推导结果如下:
类型 | 指导思想 | ParamType | T | 是否保留const/volatile属性 |
T | 传值/复制 | int | int | 否 |
T&, T* | 传引用/指针 | int&, int* | int | 是 |
T&& | 完美转发 | int& (lvalue), int&& (rvalue) | int& (lvalue), int (rvalue) | 是 |
其中,T的类型推导指导思想为确保f()的传参方式为传值,T&/T*则为传引用/指针,而T&&则是为了确保f()可以实现完美转发。 当模板参数类型为T时,const/volatile属性都不会被保留,这是传值复制规则规定的。 例如,对于f(T), 若入参为const char * const ptr, 则ParamType和T推导结果都是const char *, 因为对const ptr进行复制时不会保留其const属性。
当实参为数组时,f(T)传值复制规则下实参将被退化为指针,而f(T&|T&&)则不会,其可以保留数组维度信息。
例如,当函数实参为int a[N]
时,类型推导结果如下:
类型 | ParamType, T | 备注 |
T | int* | 退化为指针 |
T&, T&& | int(&)[N] | 保留了数组维度信息 |
函数实参也可能会被退化为函数指针,其在类型推导时会遵循和数组实参类似的规则。
例如,当函数实参为void f(int)
时,类型推导结果如下:
类型 | ParamType, T | 备注 |
T | void (*)(int) | 退化为函数指针 |
T&, T&& | void (&)(int) |
2. auto
auto类型推导规则和template参数类型推导基本一致,它们之间的映射的关系如下:
auto | T | 传值/复制 |
auto& | T& | 传引用 |
auto&& | T&& | 完美转发 |
auto和template参数类型推导规则唯一不同之处在于当实参为初始化列表时(如auto il = {1, 2, 3}
),此时auto的推导结果为std::initializerlist<T>, 而函数模板参数推导则会失败。
此外,当auto作为函数返回值或lambda入参类型时,auto类型推导规则将和template参数类型推导规则完全一致。
3. decltype
decltype可以原汁原味地不加任何修饰地返回变量名的类型, 其类型推导规则如下:
入参类型 | 推导结果 |
变量名 | 变量名对应的变量类型 |
lvalue表达式 | T& |
xvalue表达式 | T&& |
prvalue表达式 | T |
此外,C++ 14还引入了decltype(auto), 其主要被用于推导函数返回值类型。 decltype(auto)采用的推导规则为decltype规则,而不是auto推导规则。 decltype(auto)可以完成一些auto不能完成的任务,比如下面代码(源自Effective Modern C++ Item 3: Understand decltype)中的模板函数出参类型推导:
template<typename Container, typename Index> decltype(auto) authAndAccess(Container& c, Index i) { authenticateUser(); return c[i]; }
若仅使用auto, 那么出参类型推导规则为传值复制,最终推导结果将不是lvalue引用而不能满足需求。 而decltype(auto)则采用decltype类型推导规则,其对lvalue表达式的推导结果为lvalue引用。
4. 参考文档
- Effective Modern C++ Chapter 1: Deducing Types
- https://en.cppreference.com/w/cpp/language/auto.html
- https://en.cppreference.com/w/cpp/language/decltype.html