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
}

In inc1,x是类型int&,因为int&T&&都是引用,但不是两者都是 r 值。

类似地, in inc2,x属于 类型int&,因为int&T&都是引用,但不是两者都是 r 值。

我的问题是y:为什么 in是类型inc1,而 in是类型?yint&inc2yint

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

4

2 回答 2

14

在这两个函数调用中,您传递给函数的是一个int &(在某种意义上:“类型的左值int”)。因此,随着 的声明inc1,编译器必须推断出TT &&您提供的参数相匹配,即int &。这样做的唯一方法是假设Tis int &,因为 then T &&is int & &&,它等价于int &。如此Tint &并且本地y被声明为这样。

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


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

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

然后,当您将其称为 as时,编译器将对参数inc3(a)应用任何必要的隐式转换以使其适合。在这种情况下,意味着从(在左值的意义上)转换到(在右值的意义上)——这被称为左值到右值的转换和有效的隐式转换。它基本上相当于将变量转换为当时它所代表的值。inc3(a)aint &inta

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

这就是为什么,虽然int &&是右值引用,T &&(其中T是模板参数)不一定是右值引用。相反,它是拟合T提供的论点的任何结果。因此,这种情况下的表达式T &&被称为通用引用(与左值或右值引用相反)——它是一个根据需要变为左值或右值的引用。

于 2013-06-04T05:52:16.260 回答
7

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

模板实参推导是通过将每个函数模板形参类型(称为 P)与调用的相应实参类型(称为 A)进行比较来完成的,如下所述。

因此,我们正在尝试在给定 A 类型的情况下计算出 P int

S14.8.2.3 继续:

如果 P 是 cv 限定类型,则 P 类型的顶级 cv 限定符将被忽略以进行类型推导。如果 P 是引用类型,则使用 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

—结束示例]

您的调用就像f(i)在示例中一样-实例化形式为f<int&>(int&)... 的函数,即Tis int&,这就是T y = x创建对 . 的引用的原因x

另请参阅 Scott Meyers 的页面http://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

于 2013-06-04T06:55:19.377 回答