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

rvalue reference in class template vs function template

#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(i); ^ main.cpp:7:5: 注意:正在初始化 'test::test(T&&) [with T = int]' 的参数 1 测试(T&& t) ^~~~

谁能解释一下为什么class模板编译不通过,而函数模板却可以? 谢谢

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

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

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

在起草 C++11 期间,建议使用与右值引用不同的语法来转发引用(而不是对两者都使用 T&& t);然而,委员会最终决定了目前的行为。

有关模板参数推导的更详细说明,包括更精确的说明何时 T&& 成为转发引用,see here -- 搜索术语 "forwarding reference" 以找到转发引用的特殊规则。

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

在 class 版本中,您手动指定 T 是什么。您明确告诉编译器 Tint。构造函数参数类型 T && 在这种情况下变为 int &&,它不能绑定到常规左值。因此错误。

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

这解释了您观察到的差异。

为了实验,在后一种情况下,您可以抑制模板参数推导并明确指定模板参数

succeed<int>(i); 

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

同样,您可以通过将模板参数指定为 int &

"simulate" 函数的 class 行为
test<int &> t(i);

相同的 "reference collapsing" 规则将使您的构造函数调用成功编译。