只有两种形式的引用:左值引用和右值引用,万能引用不是一种引用类型,而是代表要么是左值引用要么是右值引用。
1、万能引用用在需要推断类型的场合,即以下两种场合:
a)用在模板
template
void tempFun(T&& t) {} //模板类型的这种用法 T && 是万能引用最常见的使用场合
b)auto推断类型
auto&& var2 = var1; //auto这种需要推断类型的地方
万能引用虽然跟右值引用的形式一样,但右值引用需要是确定的类型,如: int && ref = x;
2、万能引用能够接收左值或右值,返回左值引用或右值引用
template
void tempFun(T&& t)
{
t = 40;
cout << t << endl;
}
int main()
{
int x = 19;
tempFun(x); //T为int, t为int & ,即左值引用, 函数模板中对t的改动为影像x的值
tempFun(30);//T为int, t为int&&,即右值引用
int &&r = 100;
tempFun(r); //虽然r绑定到一个右值,但r变量本身是个左值(因为他出现在=的左边)
return 0;
}
尤其要注意:int &&r = 100; 这种形式r虽然绑定右值,但r本身是个左值。3、auto类型推断
3、auto会推断类型,所以也可以用万能引用
auto推断类型是在编译器就确定了,不是在运行期。4、
int v1 = 100;
auto && r1 = v1; //auto为int,r1的类型为int &
auto&& r2 = 200; //auto为int,r2的类型为int &&
4、注意区分万能引用和右值引用,只有形如 T && 的形式才是万能引用
如下void fun(T && t); 中T&&并不是万能引用,因为T的类型在模板实例化时已经确定,当实例函数void fun(T && t);时 T的类型已经确定。
template
class A
{
void fun(T && t); //这里是右值引用
};
但如下是万能引用:
template
class A
{
template
void fun(U && u); //这里是万能引用
};
5、与c++11之前的标准比较....
老版本c++,是没有右值引用这个概念的,故也没有万能引用的概念,如果我要实现技能接收左值又能接收右值,需要用const重载....
template
void tempTest(T & t) {} //接收左值
template
void tempTest(const T & t) {} //虽然能同时匹配左值和右值,但会给左值强制const
使用场景:通过函数模板调用另外一个函数,如:
template
void tempFun(F f, T && t1, U && t2)
{
f(t1, t2);
}
我们已经知道模板中使用万能引用是有益的,这样既能接收左值也能接收右值。但对于函数内部来说不管接收的是左值还是右值,模板函数内部对于形参都是左值(T && t1=var, t1本身是左值)。
此时如果f函数的第一个参数需要右值,我们必须这样调用:f(std::move(t1), t2);
但模板是通用的,我们不能直接用std::move()写死,这样就不能调用接收左值的函数了。
c++标准提供std::forward<>模板类来保持参数的原有类型,代码如下:
template
void tempFun(F f, T && t1, U && t2)
{
f(std::forward(t1), std::forword(t2));
}
这样传过来的参数t1、t2的类型被直接转发到函数f()中去,称为完美转发。
这样传递左值还是右值就有调用tempFun()函数参数的调用者来确定了。
完美应该把以上模板作为范例,即模板类型参数都用T&&格式的万能引用,需要调用函数的时候,参数都用std::forward<>()来进行传递。