C++ c+中的参考解释+;
考虑以下代码:C++ c+中的参考解释+;,c++,c++11,C++,C++11,考虑以下代码: #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
#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
}
在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上都观察到了这一点。在这两个函数调用中,传递给函数的是
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
(右值意义上)–这称为左值到右值转换和有效的隐式转换。它基本上相当于将变量a
转换为它当时表示的值
但是,当您声明一个模板时,例如问题中的inc1
和inc2
,并且函数参数是根据模板参数定义的,那么编译器将不会(或者不仅仅)尝试对参数应用隐式转换以使其适合。相反,它将选择参数类型参数T
,以便与您提供的参数类型匹配。这方面的规则很复杂,但是在T&&
类型参数声明的情况下,它们的工作方式如上所述。(在纯T
参数声明的情况下,左值参数仍将进行左值到右值的转换,T
将被推断为int
,而不是int&
)
这就是为什么当
int&
是右值引用时,T&
(其中T
是模板参数)不一定是右值引用。相反,它是将T
拟合到提供的参数所得到的结果。因此,在这种情况下,表达式T&&
被称为通用参考(与左值或右值参考相反)–它是一种根据需要变成左值或右值参考的参考。S14.8.2.1[临时扣减调用]表示:
通过将每个函数模板参数类型(称为p)与
调用的相应参数的类型(称为A),如下所述
所以,我们试图计算出一个p,给定一个int
类型
S14.8.2.3继续:
如果p是cv限定类型,则p类型的顶级cv限定符将被忽略以进行类型推断。如果P是a
引用类型,P引用的类型用于类型推断。如果P是对cv非限定模板参数的右值引用,且参数是左值,则类型“对a的左值引用”将代替类型演绎。[示例:template int f(T&&);//为什么说我们传递一个int&
?这很明显吗?@perreal是的。传递的参数是声明为int
的变量:int a=10;
和int b=10;
。当这些变量用作函数调用的参数时,它们的类型是int&
(即,它们是左值引用)。更正我之前的评论:这些变量的类型确实是int&
,因此它们是左值(不是左值引用。该术语仅适用于声明为a
或b
引用的新变量)。你没有传递一个int&
,你传递的是一个int
!变量的类型是由它们的声明确定的,不依赖于使用它们的上下文。此外,你把表达式的值和表达式的类型混在了一起。正确的答案是Tony D的答案。@MWid我的推理没有说明任何关于e角色const
在模板参数类型推导中起作用。
inc3(int x) { /*...*/ }
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