#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:在函数&#39; int main()&#39;: main.cpp:20:18:错误:无法绑定&#39; int&#39;左值&#39; int&amp;&amp;&#39; t(i); ^ main.cpp:7:5:注意:初始化&#39; test :: test(T&amp;&amp;)[with T = int]&#39; 测试(T&amp; t) ^ ~~~
有人可以解释为什么类模板无法编译但功能模板可以吗? 感谢。
答案 0 :(得分:4)
在succeed
中,T&& t
是转发参考,而不是右值参考。但在test
中,它是一个右值参考。
仅当参数为T&&
且T
是该函数的模板参数时才会发生转发引用。在您的代码T
中是封闭类的模板参数,因此它不会被视为转发引用。
转发引用可以绑定左值和右值。
在起草C ++ 11期间,建议使用不同的语法来转发引用而不是rvalue引用(而不是使用T&& t
);然而委员会最终解决了目前的行为。
有关模板参数扣除的更详细说明,包括更精确的T&&
何时成为转发参考的说明,see here - 搜索术语&#34;转发参考&#34;找到转发参考的特殊规则。
答案 1 :(得分:3)
您的困惑可能源于您假设T
int
T
。这就是为什么你认为这两种情况类似的原因。实际上他们不是。
在班级版本中,您手动指定T
是什么。您明确告诉编译器int
是T &&
。在这种情况下,构造函数参数类型int &&
变为T
,它不能绑定到常规左值。因此错误。
在函数版本中,您没有告诉编译器T
是什么,而是期望编译器推导它。在像您这样的情况下,语言是故意设计为将int &
推断为int
(注意:不是int &
,而是T
)。将int &
推断为T &&
后,所谓的"reference collapsing" rules会导致函数参数类型int &
变为i
- 一个普通的左值引用。此参数可以成功绑定到左值参数succeed<int>(i);
。
这解释了你观察到的差异。
为了实验,在后一种情况下,您可以抑制模板参数推导并明确指定模板参数
T
这将强制指定int
为int &
并导致与类版本完全相同的错误,原因完全相同。
同样,您可以通过将模板参数指定为test<int &> t(i);
request.user
相同的“引用折叠”规则将使您的构造函数调用成功编译。