01 理解模板类型推导
01 理解模板类型推导
1.1 函数模板与衍生
下面是一个模板函数的代码,其中ParamType
是一个与T
有关的类型。
template<typename T> |
(Easy)第一种情况: ParamType 是个非通用的引用或者是一个指针
1. T&
函数模板如下:
template<typename T> |
声明的变量如下:
int x = 27; // x是一个int |
调用产生的模板参数为:
f(x); // T是int,param的类型时int& |
2. const T&
函数模板如下:
template<typename T> |
声明的变量如下:
int x = 27; // x是一个int |
调用产生的模板参数为:
f(x); // T是int,param的类型时const int& |
3. T*
函数模板如下:
template<typename T> |
声明的变量如下:
int x = 27; // x是一个int |
调用产生的模板参数为:
f(x); // T是int,param的类型时int* |
第二种情况: ParamType 是个万能引用(Universal Reference)
template<typename T> |
第三种情况: ParamType 既不是指针也不是引用
1. 传递值和引用
当 ParamType
既不是指针也不是引用,我们把它处理成pass-by-value
:
template<typename T> |
即,不论传递的是引用还是值,都会将值拷贝一份,并忽略cv限定符:
int x = 27; // 和之前一样 |
2. 传递指针
指针的特殊情况
指针被传递时,指针本身依旧会发生拷贝,即失去cv属性并拷贝一个新的指针,但是指针指向的内存对应的属性会被保留:
template<typename T> |
按照按值传递的类型 推导法则, ptr
的 const
特性会被忽略,这样 param
的推导出来的类型就是 const char*
, 也就是一个可以被修改的指针,指向一个 const
的字符串。 ptr
指向的东西的 const
特性被加以保留,但是 ptr
自己本身的 const
特性会被忽略,因为它要被重新复制一份而创建了一个新的指针 param
。
3. 传递数组(important)
3.1 正常引入
使用正常的数组作为参数:
const char name[] = "J. P. Briggs"; // name的类型是const char[13] |
结果是,数组退化为了指针参数。
3.2 模板参数
因为数组参数声明会被当做指针参数,传递给模板函数的按值传递的数组参数会被退化成指针类型。这就意味着在模板 f
的调用中,模板参数 T 被推导成 const char*
:
const char name[] = "J. P. Briggs"; // name的类型是const char[13] |
3.3 如果是T&(important)
尽管函数不能被真正的定义成参数为数组,但是可以声明参数是数组的引用!(例如:int (&a)[10]
)
所以如果我们修改模板 f 的参数成引用:
template<typename T> |
然后传一个数组给他:
f(name); // 传递数组给f,T = const char (&)[13] |
T 最后推导出来的实际的类型就是数组!类型推导包括了数组的长度,所以在这个例子里面, T 被推导成了 const char [13]
,函数 f 的参数(数组的引用)被推导成了 const char (&)[13]
。是的,语法看起来怪怪的,但是理解了这些可以升华你的精神。
3.4 数组引用T& 和 带数组长度N的模板(important)
有趣的是,声明数组的引用可以创造出一个推导出一个数组包含的元素长度的模板:
// 在编译的时候返回数组的长度(数组参数没有名字, |
备注:优先使用std::array
std::array<int, arraySize(keyVals)> mappedVals; // mappedVals长度是七 |
4. 传递函数
数组并不是C++唯一可以退化成指针的东西。函数类型可以被退化成函数指针,和我们之前讨论的数组的推导类似,函数可以被退化成函数指针:
void someFunc(int, double); // someFunc是一个函数 |