条款1 理解模板类型推导

  1. template<typename T>
  2. void f(ParamType param);
  3. f(expr); //调用

通常 ParamType 包含一些类型的装饰,比如 const 或 reference 特性。


第一种情况:ParamType是个非通用的引用或者是一个指针

  1. 如果 expr 的类型是引用在类型推导过程中忽略引用的部分

  2. 利用 expr 的类型和 ParamType 对比去判断T的类型

  1. template<typename T>
  2. void f(T& param); // param是一个引用类型
  3. int x = 27; // x是一个int
  4. const int cx = x; // cx是一个const int
  5. const int& rx = x; // rx是const int的引用
  6. f(x); // T是int,param的类型是int&
  7. f(cx); // T是const int,
  8. // param的类型是const int&
  9. f(rx); // T是const int
  10. // param的类型时const int&

第二种情况:ParamType 是个万能引用(万能引用参数声明类型是T&&)

  1. 如果 expr 是左值**T****ParamType**都会被推导为左值引用

  2. 如果 expr 是右值,执行普通法则

  1. template<typename T>
  2. void f(T&& param); // param现在是一个通用的引用
  3. int x = 27;
  4. const int cx = x;
  5. const int& rx = x;
  6. f(x); // x是左值,所以T是int&
  7. // param的类型也是int&
  8. f(cx); // cx是左值,所以T是const int&
  9. // param的类型也是const int&
  10. f(rx); // rx是左值,所以T是const int&
  11. // param的类型也是const int&
  12. f(27); // 27是右值,所以T是int
  13. // 所以param的类型是int&&

第三种情况:ParamType既不是指针也不是引用(pass-by-value)

意味着 param 就是 expr 的一份拷贝——一个完全新的对象

  1. 如果 expr 的类型是引用,将会忽略引用的部分

  2. 在忽略 expr 的引用特性之后,如果 expr 还是个 const 的,也要忽略 const (volatile也要忽略)

  1. template<typename T>
  2. void f(T param); // param现在是pass-by-value
  3. int x = 27;
  4. const int cx = x;
  5. const int& rx = x;
  6. f(x); // T和param的类型都是int
  7. f(cx); // T和param的类型也都是int
  8. f(rx); // T和param的类型还都是int

如果传递的是一个const char*_ _const参数,那么经过拷贝后,指针本身的 const 特性将消失,只剩下对象的 const 特性,即被推导为const char*,指针可以指向不同之物。


image.png

条款2 理解auto类型推导

auto 类型推导就是模板类型推导,auto 相当于模板中的 T,而变量类型相当于 ParamType

例外对待花括号初始化的行为

  1. auto x1 = 27; // 类型是int,值是27
  2. auto x2(27); // 同上
  3. auto x3 = { 27 }; // 类型是std::intializer_list<int> // 值是{ 27 }
  4. auto x4{ 27 }; // 同上

当使用一对花括号来初始化一个auto类型变量时,推导得到的类型是std::intializer_list这一点导致花括号和auto容易被误用,而模板类型推导如果参数是花括号会导致代码不能通过编译
image.png


C++14允许auto作为函数返回值和 lambda 参数,但是其实是复用了模板的类型推导,然而模板并不能将花括号初始化推导为 std::intializer_list 而 auto 可以。所以返回一个花括号初始化会无法编译。

image.png

条款3 理解decltype

  • 给定一个变量名或表达式,decltype可以得出类型

decltype的推导规则和auto、模板的规则不一样,是什么就返回什么

C++11允许对单表达式的 lambda 式的返回值类别进行推导,而C++14扩大到所有函数。因此使用auto推导返回值类型的时候,如果是C++11,如果要使用函数形参的类型,就必须使用尾置返回值类型(因为一定要确保用到函数形参时它已经出现);在C++14中,则不需要。

auto作为返回值的时候会剥离对象的引用特性,所以返回**decltype(auto)**,它也可以用于变量声明。


对于一个变量名,decltype返回左值,对于一个比变量名更复杂的左值表达式decltype返回左值引用
image.png·
image.png

条款4 知道如何查看类型推导

  • std::type_info::name的特化指定了类型会被当做他们传递给模板函数的时候值传递的参数,所以会丢掉 reference ,如果丢弃后还有 const 属性,那么 const 属性也会被忽略。

    所以typeid有时候并不可靠。