请考虑以下代码:
#include <iostream>
template<typename T>
void inc1(T&& x)
{
T y = x;
++y;
}
template<typename T>
void inc2(T& x)
{
T y = x;
++y;
}
int main()
{
int a = 10;
inc1(a); // a is now 11
int b = 10;
inc2(b); // b remains 10
}
Run Code Online (Sandbox Code Playgroud)
替换后我们有
void inc1(int& x)
{
int& y = x; // reference to x
++y; // increments x
}
void inc2(int& x)
{
int y = x; // copy of x
++y; // increments the copie
}
Run Code Online (Sandbox Code Playgroud)
在inc1,x是一个类型的int&,因为这两个int&和T&&的参考但不能同时是R值.
同样,在inc2,x是一个类型的int&,因为双方再次int&和T&有参考,但不能同时是R值.
我的问题是y:为什么in inc1,y类型int&,而in inc2,y是类型int?
我在gcc 4.8.1和microsoft v110以及v120_ctp上观察到了这一点.
jog*_*pan 14
在两个函数调用中,传递给函数的是int &(在某种意义上:"类型的左值int").因此,呈现的声明inc1,编译器必须推断T,使得T &&您所提供的参数,即相匹配int &.要做到这一点的唯一方法是假设T就是int &,因为那样的话T &&就是int & &&,这相当于int &.所以T变得int &和本地y被宣布为这样.
在另一方面,在inc2中,编译器必须推断T,使得T &您所提供的参数类型,这仍然是匹配的int &.这是最容易做到的假设T是简单的int,所以这就是你得到的本地类型y然后.
回复一些注释(同时已被删除):如果您有一个具有预定义参数类型的函数,例如
inc3(int x) { /*...*/ }
Run Code Online (Sandbox Code Playgroud)
那么,当你调用这个例如作为inc3(a),编译器将应用于任何隐式转换必要的参数,使之适合.在这种情况下,inc3(a)这意味着a从int &(在左值的意义上)转换为int(在rvalue的意义上) - 这被称为左值到右值的转换和有效的隐式转换.它基本上等于将变量a转换为它当时所代表的值.
但是,当你声明一个模板,如inc1和inc2从问题中,函数参数在模板参数来定义,那么编译器不会,或者不仅,尝试隐式转换申请的说法,使之适合.相反,它将选择参数类型参数,T以便它与您提供的参数类型匹配.对此的规则很复杂,但在T &&类型参数声明的情况下,它们的工作方式如上所述.(在纯T参数声明的情况下,左值参数仍将进行左值到右值的转换,并且T将被推断为int,而不是int &.)
这就是为什么,虽然int &&是右值引用,T &&(其中T是模板参数)不一定是右值引用.相反,它是适合T所提供的论证的任何结果.因此,T &&在这种情况下的表达式被称为通用引用(与左值或右值引用相反) - 它是一个引用,根据需要变为左值或右值.
S14.8.2.1 [temp.deduct.call]说:
模板参数推导是通过将每个函数模板参数类型(称为P)与调用的相应参数的类型(称为A)进行比较来完成的,如下所述.
所以,我们试图找出一个P给定的A类型int.
S14.8.2.3继续:
如果P是cv限定类型,则类型推导将忽略P类型的顶级cv限定符.如果P是引用类型,则P引用的类型用于类型推导.如果P是对cv-nonqualified模板参数的rvalue引用,并且参数是左值,则使用类型"对A的左值引用"代替A来进行类型推导.[例如:
template <class T> int f(T&&); // <--- YOUR TEMPLATE IS LIKE THIS
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&) // <--- YOUR CALL IS LIKE THIS
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue
Run Code Online (Sandbox Code Playgroud)
- 末端的例子]
您的来电是一样f(i)的例子-它实例形式的函数f<int&>(int&)...即T就是int&,这就是为什么T y = x创建一个参考x.
另见Scott Meyers的页面http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers