类模板与函数模板中的右值引用

rvalue reference in class template vs function template

本文关键字:引用 函数模板      更新时间:2023-10-16
#include <iostream>
template <typename T>
class test
{
public:
    test(T&& t)
    {
    }
};
template <typename T>
void succeed(T&& t)
{
}
int main()
{
    int i = 1;
    test<int> t(i);    // failed to compile
    succeed(i);        // OK
    return 0;
}

GCC 5.2错误:main.cpp:在函数int main()中:Main.cpp:20:18:错误:无法将'int'左值绑定到'int&&'测试t(我);^main.cpp:7:5:注意:初始化'test::test(T&&) [with T = int]'的参数1测试(T&和;t)^ ~ ~ ~

谁能解释为什么类模板不能编译,而函数模板是可以的?谢谢。

succeed中,T&& t转发引用,而不是右值引用。但在test中,它是一个右值引用。

只有当参数为T&&时才会发生转发引用,T是该函数的模板参数。在您的代码中,T是封闭类的模板参数,因此它不算作转发引用。

转发引用可以绑定左值和右值。

在c++ 11的起草过程中,建议使用不同的语法来转发引用和右值引用(而不是对两者都使用T&& t);然而,委员会最终决定采用目前的做法。

关于模板参数推导的更详细的描述,包括T&&何时成为转发引用的更精确的规范,请参见这里——搜索"转发引用",查找转发引用的特殊规则

您的困惑可能源于您的假设,即在这两种情况下T都是int。这就是为什么你认为这两个案例是相似的。事实上,他们不是。

在类版本中,您手动指定T是什么。您显式地告诉编译器Tint。在本例中,构造函数参数类型T &&变为int &&,不能绑定到常规左值。因此出现了错误。

在函数版本中,您不告诉编译器T是什么,而是期望编译器推断出它。在像您这样的情况下,语言被故意设计为将T推断为int &(注意:不是int,而是int &)。一旦T被推导为int &,所谓的"引用崩溃"规则会导致函数参数类型T &&变成int &——一个普通的左值引用。该参数可以成功绑定左值参数i

这就解释了你观察到的差异。

为了便于实验,在后一种情况下,您可以抑制模板实参的推导并显式指定模板实参

succeed<int>(i); 

这将强制指定Tint,并导致与类版本中相同的错误,原因完全相同。

同样,您可以通过将模板参数指定为int & 来为您的类"模拟"函数的行为。
test<int &> t(i);

同样的"引用折叠"规则将使构造函数调用编译成功。