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
是什么。您明确告诉编译器 T
是 int
。构造函数参数类型 T &&
在这种情况下变为 int &&
,它不能绑定到常规左值。因此错误。
在函数版本中,您不会告诉编译器 T
是什么,而是希望编译器 推导 它。在像您这样的情况下,语言被故意设计为将 T
推断为 int &
(注意:不是 int
,而是 int &
)。一旦 T
被推导为 int &
,所谓的 "reference collapsing" rules 导致函数参数类型 T &&
变成 int &
- 一个普通的左值引用。此参数可以成功绑定到左值参数 i
.
这解释了您观察到的差异。
为了实验,在后一种情况下,您可以抑制模板参数推导并明确指定模板参数
succeed<int>(i);
这将强制指定 T
为 int
,并出于同样的原因导致与 class 版本中完全相同的错误。
同样,您可以通过将模板参数指定为 int &
"simulate" 函数的 class 行为
test<int &> t(i);
相同的 "reference collapsing" 规则将使您的构造函数调用成功编译。
#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
是什么。您明确告诉编译器 T
是 int
。构造函数参数类型 T &&
在这种情况下变为 int &&
,它不能绑定到常规左值。因此错误。
在函数版本中,您不会告诉编译器 T
是什么,而是希望编译器 推导 它。在像您这样的情况下,语言被故意设计为将 T
推断为 int &
(注意:不是 int
,而是 int &
)。一旦 T
被推导为 int &
,所谓的 "reference collapsing" rules 导致函数参数类型 T &&
变成 int &
- 一个普通的左值引用。此参数可以成功绑定到左值参数 i
.
这解释了您观察到的差异。
为了实验,在后一种情况下,您可以抑制模板参数推导并明确指定模板参数
succeed<int>(i);
这将强制指定 T
为 int
,并出于同样的原因导致与 class 版本中完全相同的错误。
同样,您可以通过将模板参数指定为 int &
test<int &> t(i);
相同的 "reference collapsing" 规则将使您的构造函数调用成功编译。