C++

Effective Modern C++(1): 类型推导

Posted by keys961 on May 28, 2022

1. 模板类型推导

考虑下面的模板:

1
2
template <typename T>
void f(ParamType param);

1.1. ParamType为指针或引用

ParamTypeT&

  • 传入intT = intParamType = int&

  • 传入const intT = const intParamType = const int&

  • 传入const int&T = const intParamType = const int&

    • 推导的&被忽略

ParamTypeconst T&

  • 传入intT = intParamType = const int&

  • 传入const intT = intParamType = const int&

    • 推导的const被忽略
  • 传入const int&T = intParamType = const int&

    • 推导的const&都被忽略

对于指针(T*const T*)也是一样

1.2. ParamType为通用引用

ParamTypeT&&,可接受左值和右值:

  • 若传入参数为左值(即可取地址),TParamType会被推导为左值引用

    • 传入intT = int&(这里推导增加了引用),折叠后ParamType = int&

    • 传入const intT = const int&(这里推导增加了引用),折叠后ParamType = const int&

    • 传入const int&T = const int&,折叠后ParamType = const int&

  • 若传入参数为右值,同第1节

    • 传入int右值,如数字:T = intParamType = int&&

折叠规则:只要有左值引用,优先折叠为左值引用

  • & + & -> &

  • & + && -> &

  • && + & -> &

  • && + && -> &&

1.3. 传值

ParamTypeT

  • 若传入参数包含引用,引用被忽略,再忽略constvolatile

    • 传入int, const int, const int&TParamType都为int

    • 这说明了传入的参数就是一个拷贝,和外面的无关了

    • 若传入const char* const,拷贝了指针,但右边的const被忽略,所以变成了指针可变、内容不可变

1.4. 数组

ParamTypeT

  • 若传入int数组,则会退化为const int*指针

ParamTypeT&

  • 若传入int数组,则T会推导为int[]

当然,更推荐直接使用std::array

1.5. 函数

会被退化为函数指针

1.6. 总结

  1. ParamTypeT&,传入引用时,推导会将引用忽略

  2. ParamTypeT&&,传入左值引用,会优先推导为左值引用

  3. ParamTypeT,引用、constvolatile会被忽略,会出现拷贝

  4. 数组和函数会被退化为指针,除非ParamType为引用

2. auto类型推导

基本同模板推导:

  • auto&:第一种情况

  • auto&&:第二种情况

  • auto:第三种情况

但是{}下会被推导为std::initialize_list

auto作为返回值或者函数参数中,则采用模板的方案

3. decltype

简单返回表达式的类型,不会加以修改。

用于模板返回类型声明,而返回类型依赖参数类型:

  • decltype(auto)标注返回值类型,而不用auto那套推导方式

  • 或在函数后用-> decltype(...)标注返回值类型

此外,对于T类型但不是单纯变量名的左值,decltype总会会返回T&,所以用于decltype(auto)返回值声明时要小心。

4. 查看类型推导结果

4.1. IDE

可通过IDE传达推导的信息,例如CLion

4.2. 编译器诊断

可通过编译器的错误提示看到类型是什么

4.3. 运行时输出

类似typeidstd::type_info::name的方案,或用第三方库

typeid(T).name(), typeid(param).name()

实际上这些都不怎么可靠