模板类型推导、auto推导
effective modern c++ 果然是神书,干货满满,简单记录下。
item1 模板推倒
典型的模板函数
temlate<class T>
void fn(ParamType param)
要记住的东西 |
---|
在模板类型推导的时候,有引用特性的参数的引用特性会被忽略 |
在推导通用引用参数的时候,左值会被特殊处理 |
在推导按值传递的参数时候,const 和/或volatile 参数会被视为非const 和非volatile |
在模板类型推导的时候,参数如果是数组或者函数名称,他们会被退化成指针,除非是用在初始化引用类型 |
当ParamType 是指针或者引用时,
-
如果
expr
的类型是个引用,忽略引用的部分。 -
然后利用
expr
的类型和ParamType
对比去判断T
的类型。
template<typename T> void f(T& param); // param是一个引用类型 int x = 27; // x是一个int const int cx = x; // cx是一个const int const int& rx = x; // rx是const int的引用 f(x); // T是int,param的类型时int& f(cx); // T是const int, // param的类型是const int& f(rx); // T是const int // param的类型时const int&
当ParamType 是右值引用时,(注意到param是左值时的特殊性)
-
如果
expr
是一个左值,T
和ParamType
都会被推导成左值引用。这有些不同寻常。第一,这是模板类型T
被推导成一个引用的唯一情况。第二,尽管ParamType
利用右值引用的语法来进行推导,但是他最终推导出来的类型是左值引用。 -
如果
expr
是一个右值,那么就执行“普通”的法则(第一种情况)
template<typename T> void f(T&& param); // param现在是一个通用的引用 int x = 27; // 和之前一样 const int cx = x; // 和之前一样 const int& rx = x; // 和之前一样 f(x); // x是左值,所以T是int& // param的类型也是int& f(cx); // cx是左值,所以T是const int& // param的类型也是const int& f(rx); // rx是左值,所以T是const int& // param的类型也是const int& f(27); // 27是右值,所以T是int // 所以param的类型是int&&
当ParamType 既不是指针也不是引用时,按照pass by value 来
-
和之前一样,如果
expr
的类型是个引用,将会忽略引用的部分。 -
如果在忽略
expr
的引用特性,expr
是个const
的,也要忽略掉const
。如果是volatile
,照样也要忽略掉(volatile
对象并不常见。它们常常被用在实现设备驱动上面。查看更多的细节,请参考条款40。)
int x = 27; // 和之前一样 const int cx = x; // 和之前一样 const int& rx = x; // 和之前一样 f(x); // T和param的类型都是int f(cx); // T和param的类型也都是int f(rx); // T和param的类型还都是int
最后再来个大杀器
//数组的引用再理解下 //int a[]={1,2,3} --> a的类型是啥? int [3] //int & rb --->rb的类型是啥? rb先与&结合,表明是个引用,然后接触int 表明是个int型的引用 //int (&ra)[]={1,2,3} -->ra的类型是啥? ra先与&结合,表明是个引用,然后接触 int [3] 是个int [3] 的引用 //那如果去掉ra的形参,如何写呢 int (&) [3] ,这是个类型,并且是int [3] 的引用 //计算数组长度 constexpr需要vs2015以上 template<class T, std::size_t SIZE> constexpr std::size_t ConstArraySize(T(&)[SIZE]) { return SIZE; } template<class T, std::size_t SIZE> std::size_t ArraySize(T(&)[SIZE]) { return SIZE; }
item2 auto 推导
能够理解item1的话,这个就好办了,这个和item1的推导几乎一模一样。
item1的关键有个T 和ParamType 需要推导,对于auto来说,T就是auto!这么说有点困难,我们例子来说明
auto x = 27; //T 是auto ParamType也是auto 即item1上的规则3,pass by value
const auto cx = x; //T 是auto ParamType是 const auto ,按照规则3
auto& rx = x; //T 是auto ParamType是 auto & 按照规则1
auto&& uref1 = x; // x是int并且是左值 // 所以uref1的类型是int& auto&& uref2 = cx; // cx是int并且是左值 // 所以uref2的类型是const int& auto&& uref3 = 27; // 27是int并且是右值 // 所以uref3的类型是int&& const char name[] = // name的类型是const char[13] "R. N. Briggs"; auto arr1 = name; // arr1的类型是const char* auto& arr2 = name; // arr2的类型是const char (&)[13] void someFunc(int, double); // someFunc是一个函数,类型是 // void (*)(int, double) auto& func2 = someFunc; // func1的类型是 // void (&)(int, double)
到此为止,我们都非常成功,接下来是auto 与模板推导不同的地方
auto x1 = 27; // 类型时int,值是27 auto x2(27); // 同上 auto x3 = { 27 }; // 类型是std::intializer_list<int> // 值是{ 27 } auto x4{ 27 }; // 同上 auto x5 = { 1, 2, 3.0 }; // 错误! 不能讲T推导成 // std::intializer_list<T> auto x = { 11, 23, 9 }; // x的类型是 // std::initializer_list<int> template<typename T> // 和x的声明等价的 void f(T param); // 模板 f({ 11, 23, 9 }); // 错误的!没办法推导T的类型 template<typename T> void f(std::initializer_list<T> initList); f({ 11, 23, 9 }); // T被推导成int,initList的 // 类型是std::initializer_list<int>
要记住的东西 |
---|
auto 类型推导通常和模板类型推导类似,但是auto 类型推导假定花括号初始化代表的类型是std::initializer_list ,但是模板类型推导却不是这样 |
auto 在函数返回值或者lambda参数里面执行模板的类型推导,而不是通常意义的auto 类型推导 |