请考虑以下代码:
#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
}
替换后我们有
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
}
在inc1
中,x
的类型为int&
,因为int&
和T&&
都是引用,但两者都不是r值。
同样,在inc2
中,x
的类型为int&
,因为int&
和T&
都是引用,但两者都不是r值
我的问题是关于y
:为什么在inc1
,y
的类型为int&
,而在inc2
,y
是输入int
?
我在gcc 4.8.1和microsoft v110以及v120_ctp上都观察到了这一点。
答案 0 :(得分: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) { /*...*/ }
然后,当你打电话给这个时,例如如inc3(a)
,编译器将对参数应用任何必要的隐式转换以使其适合。在inc3(a)
的情况下,这意味着将a
从int &
(在左值意义上)转换为int
(在右值意义上) - 这称为左值 - to-rvalue转换和有效的隐式转换。它基本上等于将变量a
转换为它当时所代表的值。
但是当您从问题中声明模板时,例如inc1
和inc2
,并且函数参数是根据模板参数定义的,那么编译器不会或不仅会尝试将隐式转换应用于参数以使其适合。相反,它将选择参数类型参数T
,使其与您提供的参数类型相匹配。对此的规则很复杂,但在T &&
类型参数声明的情况下,它们的工作方式如上所述。 (在纯T
参数声明的情况下,左值参数仍将进行左值到右值的转换,T
将推断为int
,而不是int &
。)
这就是为什么虽然int &&
是右值引用,T &&
(其中T
是模板参数)不一定是右值引用。相反,它是将T
拟合到所提供的参数的任何结果。因此,在这种情况下,表达式T &&
被称为通用引用(与左值或右值引用相反) - 它是一个引用,根据需要变为左值或右值。 / p>
答案 1 :(得分:7)
S14.8.2.1 [temp.deduct.call]说:
模板参数推导是通过比较每个函数模板参数类型(称之为P)来完成的 调用的相应参数的类型(称之为A),如下所述。
所以,我们试图找出一个给定类型为int
的P的P。
S14.8.2.3继续:
如果P是cv限定类型,则类型推导将忽略P类型的顶级cv限定符。如果P是a 引用类型,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
-end example]
您的调用与示例中的f(i)
类似 - 它实例化f<int&>(int&)
形式的函数...即T
为int&
,这就是为什么{{ 1}}创建对T y = x
的引用。
另见Scott Meyers在http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
的页面