C++11之后的类型推导规则变得比较复杂,不太好理解。

Scott Meyers的这篇演讲/ppt基本上在一小时内说明白了一个正常C++程序员需要了解的规则:

C++TypeDeductionandWhyYouCareCppCon2014.pdf

image.png

模板类型推导(auto类型推导)

auto类型推导和模板类型推导应用相同的规则。

解释模板类型推导之前,先明确定义:

  1. template <typename T> // T 是要推导的类型
  2. void f(ParamType param) // ParamType 也是要推导的类型,是个和T有关的类型,
  3. // 比如 void f(T& param),这里T& 就是 ParamType
  4. f(expr) // 调用模板的代码,编译器要为这个调用推导合适的类型

上面涉及到两个类型的推导,分别是 T 和 ParamType,它们常常是不一样的(当然也可能一样)。

推导规则可以分为下面三类来讨论:

  • ParamType是一个reference或者pointer,但不是universal reference;
    • 这是最简单的情况,”it just works”
  • ParamType是一个universal reference;
    • 和上面的规则类似,只不过接受右值引用了;
  • ParamType既不是reference也不是pointer;
    • 这种情况,ParamType是by-value传递的,和引用方式的不同在于by-value推导出来的类型不会保留c-v qualifier;

auto和模板类型推导完全一致,除了对initializer-list的规则以外:

  • 模板类型推导不接受 initializer-list,试图将 {1, 2, 3} 这样的字面量传递给模板类型是错误;
  • auto类型推导接受initializer-list,会将 {1, 2, 3} 这样的字面量推导为类型 std::initializer_list,不过,只有auto a = {1, 2, 3} 这样的写法才会合法的在各个版本将auto类型推导为std::initializer_list ,因为不加等号的版本和C++之前定义的 变量初始化语法 冲突了,比如: auto a{1},究竟应该被推倒为auto=int,还是auto=std::initializer_list
    • 但是注意,{1, 2, 3} 这样的字面量在标准中被称作 inizializer_list,但initializer_list不是类型,std::initializer_list才是类型。

Lambda Capture 类型推导

Lambda Capture 类型推导可以分三类讨论:

  • By reference,和模板类型推导规则一样;
  • C++14 init capture:和auto类型推导规则一样;
  • By value:和模板类型推导一样,但会保留c-v qualifier;

根据上面的规则,可以看出,其实C++ 14 init capture和by value的区别就在于下面这点:
image.png

decltype类型推导

decltype会给出某个名称声明的类型。如果使用decltype推导没有名称的值的类型,decltype会给出这个值实际表现出来的类型。这句话的意思是:
image.png

方法声明 auto 返回值类型推导

这里我们也分两类讨论:

  • 方法返回值类型为auto
    • 类型推导规则和模板类型推导规则一样(注意,和auto推导规则不一样,也就是说方法返回值永远不会被推导为std::initializer_list)
  • 方法返回值类型为decltype(auto)
    • 类型推导规则和decltype规则一样,但需要注意的是,这会导致方法返回类型容易受到方法return语句写法的影响,也就是说,return返回的是一个表达式,或者是一个变量名,会导致推导出来的类型不一样:

image.png