第一种 普通左值引用类型
template<typename T>
void change(T& x) { // 函数参数是模板类型参数的一个左值引用
x = 12;
}
函数参数是模板类型参数的一个左值引用:
- 只允许传递一个左值(此时T会被推断为左值的类型,而函数的实参则是这个类型的左值引用),若传右值,会报错:
int x = 12;
change(x);
cout << x << endl; // 正确,x为int,T=int,实参为int&, x会被修改为12
change(1); // 编译错误
/*
testch16.cpp(40): error C2664: “void change<int>(T &)”: 无法将参数 1 从“int”转换为“int &”
1> with
1> [
1> T=int
1> ]
*/
- 左值可以是const类型,比如:(传参没问题,但是会编译报错,因为T会被推断为const int型)
const int x = 12;
change(x);
cout << x << endl;
/*
error C3892: “x”: 不能给常量赋值
testch16.cpp(37): note: 参见对正在编译的函数 模板 实例化“void change<const int>(T &)”的引用
1> with
1> [
1> T=const int
1> ]
*/
第二种 const 左值引用类型
则上面的三种传参皆可通过编译
template<typename T>
void change2(const T& x) {
x = 12;
}
int x = 1;
const int cx = 1;
change2(x); // T=int 实参为const int&
cout << x << endl;
change2(cx);// T=int 实参为const int&
cout << x << endl;
change2(1);// T=int 实参为const int& 右值可以传给一个const 左值引用
当然在函数内部编译器会发现试图修改底层const的行为并抛出错误
第三种 函数参数是右值引用类型
template<typename T>
void change3(T&& x) {
x = 12;
}
直接传右值引用即可
change3(22); // T=int 实参是int&&
int&& xx = 22;
cout << xx << endl;
change3(xx); // T=int 实参是int&& xx所绑定的值被改变
cout << xx << endl;
给这个模板参数传左值,问题就出现了:——右值引用是不可以被绑定到左值上的。但这里有两个例外:
- 模板类型参数T的推断:若传给函数一个X类型的左值,且函数的参数是模板类型参数T的右值引用T&&时,T会被推断为X&,而不是X;
- 关于多引用的“折叠”:上面得出函数的实参为X& &&—— X的左值引用的右值引用——,通过“折叠”规则,会得出函数的实参为X&:
X& & -> X& X&& & -> X& X& && -> X& X&& && -> X&&
根据上面两条规则,即可对下面这样的调用推断模板类型参数T和函数实参的类型:
const int yy = 22;
change3(yy);
//yy是const int;T=const int&; 函数的实参是const int& &&,折叠为 const int & 编译器发现常量被修改,抛错误
/*
testch16.cpp(30): error C3892: “x”: 不能给常量赋值
testch16.cpp(57): note: 参见对正在编译的函数 模板 实例化“void change3<const int&>(T)”的引用
1> with
1> [
1> T=const int &
1> ]
*/