通用引用(万能引用)
通用引用通常表示为 T&&,其中 T 是一个模板参数类型(模板中的&&表示通用引用)。&& 虽然通常表示一个右值引用,但当T由模板参数推导而来时,其既可以绑定左值,又可以绑定右值。当 T 是一个模板参数类型,并且T是推导出来的类型,T&&才是通用引用。
万能引用 既可以接收左值,又可以接收右值。实参是左值,通用引用就是左值引用(引用折叠)实参是右值,通用引用就是右值引用。
(引用折叠 :当其传入参数为左值时,&&会折叠成&;当传入参数为右值时,&&不折叠照常接收)
void Fun(int& x){cout<<"左值引用"<< endl; }
void Fun(const int& x){cout<<"const左值引用"<< endl; }
void Fun(int&& x) {cout<<"右值引用"<<endl; }
void Fun(const int&& x) {cout<<"const右值引用"<< endl; }
// 万能引用:既可以接收左值,又可以接收右值
// 实参左值,他就是左值引用(引用折叠)
// 实参右值,他就是右值引用
template<typename T>
void PerfectForward(T&& t){
Fun(t);
}
int main(){
PerfectForward(10); // 右值
int a;
PerfectForward(a);
return 0;
}
(2)模板类型推导
理解auto
的模板类型推导如果你不介意浏览少许伪代码,可以考虑像这样一个函数模板:
template<typename T>
voidf(ParamType param);
它的调用看起来像这样
f(expr); //使用表达式调用f
编译期间,编译器使用expr
进行两个类型推导:一个是针对T
的,另一个是针对ParamType
。这两个类型通常不同,因为ParamType
包含一些修饰,如const
和引用修饰符。
对于模板函数:
template<typename T>
void f(ParamType param);
编译器需要完成两步类型推导:
(1)推导 T 的类型
T是模板参数,当调用 f 函数并传递一个参数时,编译器会尝试根据该参数来确定 T 的具体类型。
如果调用 f(10),编译器会推断 T 是 int。
(2)推导 ParamType 的类型
ParamType 是函数参数的类型,它可以是 T 本身,也可以是对 T 进行了一些修饰后的类型,比如加上 const、&(引用)、*(指针)等。例如,假设定义如下函数模板
template<typename T>
void f(const T& param);
ParamType 是 const T&,T 的常量引用。如果调用 f(10),编译器会推断 T 是 int,ParamType 是 const int&。
template<typename T>
void f(const T& param) {
// 函数体
}
调用示例1
int x = 10;
f(x);
编译器推导 T 为 int,ParamType 为 const int&。
调用示例 2
double y = 3.14;
f(y);
编译器推导 T 为 double,ParamType 为 c