c++中的引用解释

Reference interpretation in c++

本文关键字:解释 引用 c++      更新时间:2023-10-16

考虑以下代码:

#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中,xint&类型,因为int&T&&都是引用,而不是都是r值。

同样,在inc2中,x的类型是int&,因为int&T&都是引用,但不都是r值。

我的问题是关于y:为什么在inc1中,yint&类型,而在inc2中,yint类型?

我在gcc 4.8.1和microsoft v110和v120_ctp上都观察到这一点。

在这两个函数调用中,传递给函数的是一个int &(在"类型为int的左值"的意义上)。因此,在给出inc1的声明后,编译器必须推导出T,使T &&与您提供的参数int &匹配。这样做的唯一方法是假设Tint &,因为T &&int & &&,相当于int &。因此,T变成了int &,而本地的y也被这样声明。

另一方面,在inc2中,编译器必须推断出T,使T &匹配您提供的参数类型,仍然是int &。最简单的方法是假设T就是int,所以这就是你得到的本地y的类型。


回复一些注释(同时已被删除):如果您有一个函数具有预定义的参数类型,例如

inc3(int x) { /*...*/ }

然后,当你调用它时,例如作为inc3(a),编译器将对参数应用任何必要的隐式转换以使其适合。在inc3(a)的情况下,这意味着将aint &(左值意义上)转换为int(右值意义上)。这称为左值到右值转换,是有效的隐式转换。它基本上相当于将变量a转换为它当时所表示的值。

但是,当您声明模板时,例如问题中的inc1inc2,并且函数实参是根据模板形参定义的,那么编译器将不会或不仅不会尝试对实参应用隐式转换以使其适合。相反,它将选择参数类型参数T ,因此它与您提供的参数类型匹配。这方面的规则很复杂,但在T &&类型参数声明的情况下,它们的工作原理如上所述。(在纯T参数声明的情况下,左值参数仍然会进行左值到右值的转换,并且T将被推断为int,而不是int &。)

这就是为什么int &&是右值引用,而T &&(其中T是模板形参)不一定是右值引用的原因。相反,它是将T拟合到所提供的参数的结果。因此,在这种情况下,表达式T &&被称为通用引用(与左值或右值引用相反)–它是一个引用,可以根据需要变为左值或右值。

S14.8.2.1 [temp.deduct.call]说:

模板实参推导是通过比较每个函数模板形参类型(称之为p)与调用的对应实参的类型(称之为A),如下所述。

所以,我们试着在给定类型为int的a的情况下计算p

S14.8.2.3继续说:

如果p是限定cv的类型,则忽略p类型的顶级cv限定符进行类型推导。如果P是a引用类型,则使用P所引用的类型进行类型推导。如果P是对cv不限定模板形参的右值引用,且实参是左值,则使用"对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&)的函数…也就是说,Tint&,这就是为什么T y = x创建了对x的引用。

参见Scott Meyers的主页http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers