情况1:ParamType 是引用或指针,但不是一个通用(万能)引用
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&
在f(rx)中,注意尽管 rx 的类型是一个引用, T 仍然被推导成了一个非引用的。这是因为 rx 的引用特性会被类型推导所忽略。
加入const关键字:
template<typename T>
void f(const T& param); // param现在是const的引用
int x = 27; // 和之前一样
const int cx = x; // 和之前一样
const int& rx = x; // 和之前一样
f(x); // T是int,param的类型是const int&
f(cx); // T是int,param的类型是const int&
f(rx); // T是int,param的类型是const int&
指针类型:
template<typename T>
void f(T* param); // param是一个指针
int x = 27; // 和之前一样
const int *px = &x; // px是一个指向const int x的指针
f(&x); // T是int,param的类型是int*
f(px); // T是const int, param的类型时const int*
情况2:ParamType是万能引用
如果 expr 是一个左值, T 和 ParamType 都会被推导成左值引用。这有些不同寻常。第一,这是模板类型 T 被推导成一个引用的唯一情况。第二,尽管 ParamType 利用右值引用的语法来进行推导,但是他最终推导出来的类型是左值引用。如果 expr 是一个右值,那么就执行“普通”的法则(第一种情况1.1)
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&&
关键的地方在于通用引用的类型推导法则和左值引用或者右值引用的法则大不相同。特殊的情况下,当使用了通用的引用,左值参数和右值参数的类型推导大不相同。这在非通用的类型推到上面绝对不会发生
情况3:ParamType既不是指针也不是引用
当 ParamType 既不是指针也不是引用,我们把它处理成pass-by-value:
template<typename T>
void f(T param); // param现在是pass-by-value
1. 和之前一样,如果 expr 的类型是个引用,将会忽略引用的部分。
2. 如果在忽略 expr 的引用特性, expr 是个 const 的,也要忽略掉 const 。如果是 volatile ,照样也要忽略掉
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
注意尽管 cx 和 rx 都是 const 类型, param 却不是 const 的。这是有道理的。 param 是一个和 cx 和 rx 独立的对象——一个 cx 和 rx 的拷贝。 cx 和 rx 不能被修改和 param 能不能被修改是没有关系的。这就是为什么 expr 的常量特性(或者是易变性)(在很多的C++书籍上面 const 特性和 volatile 特性被称之为CV特性——译者注)在推导 param 的类型的时候被忽略掉了: expr 不能被修改并不意味着它的一份拷贝不能被修改。认识到 const (和 volatile )在按值传递参数的时候会被忽略掉。正如我们所见,引用的 const 或者是指针指向 const , expr 的 const 特性在类型推导的过程中会被保留。但是考虑到 expr 是一个 const 的指针指向一个 const 对象,而且 expr 被通过按值传递传递给 param :
template<typename T>
void f(T param); // param仍然是按值传递的(pass by value)
const char* const ptr = // ptr是一个const指针,指向一个const对象
"Fun with pointers";
f(ptr); // 给参数传递的是一个const char * const类型
这里,位于星号右边的 const 是表明指针是常量 const 的: ptr 不能被修改指向另外一个不同的地址,并且也不能置成 null 。(星号左边的 const 表明 ptr 指向的——字符串——是 const 的,也就是说字符串不能被修改。)当这个 ptr 传递给 f ,组成这个指针的内存bit被拷贝给 param 。这样的话,指针自己( ptr )本身是被按值传递的。按照按值传递的类型推导法则, ptr 的 const 特性会被忽略,这样 param 的推导出来的类型就是 const char* ,也就是一个可以被修改的指针,指向一个 const 的字符串。 ptr 指向的东西的 const 特性被加以保留,但是 ptr 自己本身的 const 特性会被忽略,因为它要被重新复制一份而创建了一个新的指针 param
要记住的东西
1,在模板类型推导的时候,有引用特性的参数的引用特性会被忽略
2,在推导通用引用参数的时候,左值会被特殊处理
3,在推导按值传递的参数时候, const 和/或 volatile 参数会被视为非 const 和非 volatile
4,在模板类型推导的时候,参数如果是数组或者函数名称,他们会被退化成指针,除非是用
在初始化引用类型
参考:Effective Modern C++