此部分内容和右值引用重复
万能引用
是否看到T&就一定是个左值引用 是
是否看到T&&就一定是个右值引用 否 — 有的模板代码里 对于类型参数的推导结果可能是引用 — T可能是个引用类型
假设右值引用是使用&&声明的,那么假设类型声明中出现&& 表示右值引用似乎是合理的。事实并非如此:
Widget&& var1 = someWidget; // here, “&&” means rvalue reference
auto&& var2 = var1; // here, “&&” does not mean rvalue reference
template<typename T>
void f(std::vector<T>&& param); // here, “&&” means rvalue reference
template<typename T>
void f(T&& param); // here, “&&”does not mean rvalue reference
类型声明当中的“&&”有的时候意味着rvalue reference,但有的时候意味着rvalue reference 或者 lvalue reference (两种都有可能)。因此,源代码当中出现的 “&&” 有可能是 “&” 的意思,即是说,语法上看着像rvalue reference (“&&”),但实际上却代表着一个lvalue reference (“&”)。在这种情况下,此种引用比lvalue references 或者 rvalue references都要来的更灵活。
Rvalue references&&只能绑定到右值上,lvalue references&除了可以绑定到左值上,特殊的常左值引用const…&可以绑定到右值上。
声明中带 “&&” 的,可能是lvalue references 或者 rvalue references 可以绑定到任何东西上。这种引用灵活也忒灵活了,值得单独给它们起个名字。我称它们为 universal references(万能引用或转发引用、通用引用)。
到底 “&&” 什么时候才意味着一个universal reference(万能引用)呢(即,代码当中的“&&”实际上可能是 “&”)
如果一个变量或者参数被声明为T&&,其中T是被推导的类型(T是模板参数 或者 auto 或者使用typedef和decltype的时候也可能会出现),那这个变量或者参数就是一个universal reference(万能引用)。
在实践当中,几乎所有的universal references都是函数模板的参数。因为auto声明的变量的类型推导规则本质上和模板是一样的,所以使用auto的时候你也可能得到一个universal references。
和所有的引用一样,你必须对universal references进行初始化,而且正是universal reference的initializer决定了它到底代表的是lvalue reference 还是 rvalue reference:
对于 template
template
如果实例化类型 T 为左值引用(或者直接传入一个左值),那 T&& 的结果仍然是左值引用——即 type& && 坍缩成了 type&。
如果 T 是一个实际类型,那 T&& 的结果自然就是一个右值引用
如果 T 是右值引用,那 T&& 的结果依然是右值引用
(下图所讲的实参参数类型 指的是实例化模板参数T所传入的类型是个左引用foo
可以发现当函数形参类型为右引用时 推导后的类型依然保持不变
foo(T&&) foo((int&&)x)-推导->foo(int&&),foo((int&)x)-推导->foo(int&) 当我们用右值引用作为函数形参,并使用自动推导机制(不显示地给定模板类型) 可以保证推导后的形参类型和传入的实参类型一样,这是个很好的性质(转发引用,万能引用)
1.万能限定必须是函数模板,可以模板参数是单个,也可以是多个模板参数,形式为T&&
2.万能引用可以接受左值,也可以接受右值,而不像前面普通函数TestLValueOrRValue一样,必须与左右引用形式对应3.万能引用的T不能被再修饰,否则转为普通右值引用,不能被cv修饰限定4.如果想在模板类中的模板函数使用万能引用,不能使用模板类的参数,否则转为普通右值引用,如下代码样例:
template<class T>
struct Test {
// a参数来源T,源自模板类,不是万能引用,为右值引用
// b参数来源U,源自模板函数,是万能引用
template<class U> // U是函数模板 T是类模板
A(T&& a, U&& b, int c);
}
1.区分万能引用和右值引用
例1
Widget&& var1 = someWidget;
auto&& var2 = var1;
因为var1是有名的所以var1是一个左值,var2的类型声明是auto&&—-T&&,T需要被推导,所以它就是一个universal reference。 并且var2是被 左值var1初始化,所以var2就被自动推导为一个左值引用(Widget&)。
当一个universal reference开始被lvalue初始化的时候,var2就变成了lvalue reference
例2
std::vector<int> v;
...
auto&& val = v[0]; // val becomes an lvalue reference (see below)
val是auto&&—-T&&,T需要被推导,所以它就是一个universal reference。
v[0]可被取地址所以是个左值, 所以val 就被自动推导为一个左值引用(int&)。
只有在发生类型推导的时候 “&&” 才代表 universal reference 吗。如果没有类型推导,就没有universal reference。这种时候,类型声明当中的“&&”总是代表着rvalue reference。
T&& 万能引用
template
void f(T&& param); // deduced parameter type ⇒ type deduction;
Widget&& 右值引用
void f(Widget&& param);
例3
template<typename T>
void f(std::vector<T>&& param); // “&&” means rvalue reference
虽然T需要推导,但是不是T&&形式。所以param是个右值引用而不是万能引用
例4
template<typename T>
void f(const T&& param); // “&&” means rvalue reference
有T&&,但是多了个const。const T&&不是万能引用而是一个右值引用。
Universal references只以 “T&&”的形式出现!即便是仅仅加一个const限定符都会使得“&&”不再被解释为universal reference
例5
template<typename MyTemplateParamType>
void f(MyTemplateParamType&& param); // “&&” means universal reference
不是说非得叫T,MyTemplateParamType&&—param是个万能引用
例6 !!!!!!!!
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type ⇒ no type deduction;
... // && ≡ rvalue reference
};
这里, T 是模板参数, 并且push_back接受一个``T&&, 但是这个参数却不是universal reference。
看push_back函数在类外是怎么声明的,就很清楚了。
template
void vector
push_back不能离开std::vector
例7—-6的具体例子
Widget makeWidget(); // factory function for Widget
std::vector<Widget> vw;
...
Widget w;
vw.push_back(makeWidget()); // create Widget from factory, add it to vw
代码中对 push_back 的使用会让编译器实例化类 std::vector
void std::vector
一旦我们知道了类是 std::vector
例8
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction;
... // && ≡ universal references
};
emplace_back 看起来需要多个参数(因为Args和args的声明当中都有…),但重点是每一个参数的类型都需要进行推导。函数的模板参数 Args 和类的模板参数T无关(Args是函数专用的模板 仅仅知道了T依旧不知道Args,所以Args依旧需要被推导,所以Args&&为万能引用),所以即使我知道这个类具体是什么(知道T),但是不知道Args,Args依旧需要推导 所以Args&&是万能引用。
template
void std::vector
实例化了T为Widget但是Args没有被实例化,Args依旧需要被推导所以Args&&是万能引用。
一个绑定到universal reference上的对象可能具有 lvalueness 或者 rvalueness,正是因为有这种二义性,所以催生了std::forward:希望在传递参数的时候,可以保存参数原来的lvalueness 或 rvalueness,即是说把参数转发给另一个函数。