Type Deduction in Modern C++
date
Aug 16, 2024
slug
type-deduct-cpp
status
Published
tags
C++
summary
type
Post
1 - 理解模板类型推导
在 C++ 中,一个模板函数的形式如下:
其中,ParamType 的形式是 T 加上修饰词,比如 const, volatile;或者加上引用或指针形式。
当传入实参 expr 时,根据 expr 的类型进行类型推导,根据 ParamType 的形式分为三种情况:
- ParamType 是指针或者引用形式,但不是万能引用:
- 如果 expr 是引用类型,先 drop 掉 expr 的引用性质,然后跟 ParamType 的形式中 T 进行模式匹配,推导 T.
- ParamType 是万能引用:
- 如果 expr 是左值,那么 T 和 ParamType 都被推导为引用类型,如果 expr 是右值则 ParamType 是右值引用类型
- ParamType 即不是指针也不是引用:
- drop 掉引用和修饰词,按值传递
- 特殊情况:当数组类型或者函数类型的实参被传给按值形式的模板参数时,会退化成指针类型,如果是引用形式的模板参数则会保留原来的类型。
- Summary:
- 在模板类型推导的过程中,实参的引用性质会被忽略;
- 对万能引用形参进行推导时,当实参时左值会特殊处理;
- 对按值传递的形参进行推导时,忽略修饰词和引用性质;
- 数组或者函数类型的实参会退化成对应的指针,除非他们被用来初始化引用。
2 - auto 类型推导
auto 类型推导的逻辑和规则基本和模板类型推导一致:
- 例如,对于 const auto& x = expr; 其中 auto 扮演函数模板中 T 的角色,修饰词,auto 和引用性质一起扮演 ParamType 的角色。
- 不同的情况:
- 对于初始化形式 auto x{expr} 或者 auto x = {expr}, 对于 auto 来说会假定为 std::initializer_list<T>,但是对于函数模板来说不会。例如对于上一节的 f 函数,如果传入一个 {expr} 那么将无法编译。
- 在函数的返回值或者 lamda 表达式的形参使用 auto 时规则按照函数模板类型推导而不是 auto 类型推导。
- Summary:
- 一般情况下 auto 的规则和函数模板类型推导相同,除了 auto 假定 {} 中的表达式时 std::initializer_list<T> 类型,而函数模板不会。
- 在函数返回值或者 lambda 表达式的形参中的 auto 意识是使用模板类型推导而不是 auto 类型推导。
3 - decltype 类型推导
decltype(expr) 用来获取 expr 的类型:
在上面的例子中,decltype 的行为时比较符合常规的预期的。decltype 的主要作用是在那些模板函数的返回值类型依赖于形参的类型的情况,比如下例:
在 C++ 14 中对于函数返回值,以及变量初始化的时候支持使用 decltype(auto):
- 在 decltype(auto) 的使用场合下,使用初始化的表达式来推导类型,也就是 decltype(auto) = decltype(expr)
Summary:
- 对于 decltype, 如果传入的表达式是 T 类型的左值表达式,那么除非它是一个名字,否则一律推导为 T&(想想 decltype(x) 和 decltype((x)) 的区别)
- C++ 14 支持 decltype(auto), 它通过初始化表达式出发来进行类型推导,使用 decltype 的推导规则。