我有这个代码,它不能编译,这是预期的
这是错误:an rvalue reference cannot be bound to an lvalue
class SomeData
{
public:
vector<int> data;
SomeData()
{
cout << "SomeData ctor" << endl;
data.push_back(1);
data.push_back(2);
data.push_back(3);
}
SomeData(const SomeData &other)
{
cout << "SomeData copy ctor" << endl;
data = other.data;
}
SomeData(SomeData &&other)
{
cout << "SomeData move ctor" << endl;
data = move(other.data);
}
~SomeData()
{
cout << "SomeData dtor" << endl;
}
void Print() const
{
for(int i : data)
cout << i;
cout << endl;
}
};
void Function(SomeData &&someData)
{
SomeData localData(someData);
localData.Print();
}
int main(int argc, char *argv[])
{
SomeData data;
Function(data); // ERROR
data.Print();
return 0;
}
但是,当我将Function()
转换为模板时,它工作正常,并使用SomeData
的复制构造函数。
template<class T>
void Function(T &&someData)
{
T localData(someData); // no more error
localData.Print();
}
这是标准的C ++行为吗?
我注意到视觉工作室在模板方面往往更宽容,所以我想知道是否可以从所有兼容的C ++ 11编译器中获得相同的行为。
答案 0 :(得分:9)
是。在模板函数的情况下,编译器推导出模板参数T
,使其与给定的参数匹配。
由于someData
实际上是左值,T
推断为SomeData &
。在类型扣除之后,Function
的声明变为
void Function(SomeData & &&)
在rules for reference collapsing之后,和SomeData & &&
变为SomeData &
。
因此,函数参数someData
成为左值引用,并按原样传递给localData
的初始化。请注意(正如@aschepler正确指出的那样)localData
被声明为T
,因此它本身就是SomeData &
类型的引用。因此,这里没有复制构造 - 只是初始化引用。
如果您希望localData
成为实际副本,则必须将类型从SomeData &
转换为SomeData
,即您必须从&
中删除std::remove_reference
类型。您可以使用template<class T>
void Function(T &&someData)
{
/* Create a copy, rather than initialize a reference: */
typename std::remove_reference<T>::type localData(someData);
localData.Print();
}
执行此操作:
#include <type_traits>
(为此,您必须{{1}}。)
答案 1 :(得分:4)
这确实是预期的行为。表单的模板函数:
template< class T >
Ret Function( T&& param )
{
...
}
遵循特殊规则(Ret可以是或不是模板,没关系)。 T&安培;&安培;被称为通用引用,它基本上可以绑定到任何东西。这是因为当模板推断开始并且param采用该形式时(注意,vector<T>&&
不是通用引用,对于任何其他模板参数都不是C<T>
),应用引用折叠规则:
T = int&amp; =&GT; T&安培;&安培; = int&amp; &安培;&安培;并且崩溃到一个int&amp;
完整的相关表格是:
& + && = &
&& + & = &
&& + && = &&
所以当你有上述功能时
int a = 5;
int b& = a;
Function( a ); // (1)
Function( b ); // (2)
Function( 3 ); // (3)
在案例1中,T = int&amp;推导出的类型是int&amp; (因为a是左值)因此Function具有以下签名:
Ret Function( int& param ) // int& && collapses to int&
在案例2中,T = int&amp;
Ret Function( int& param ) // int& && collapses to int&
在案例3中,T = int
Ret Function( int&& param )
这种崩溃规则是委员会认为合理的完美转发工作。您可以在this Scott Meyers's video
中找到长篇故事答案 2 :(得分:2)
只是向您保证:这不是MSVC问题,这实际上是预期的行为。
在模板中,&&
在应用于模板类型参数时具有不同的含义。它被称为通用引用。
我可以解释一下,但是this article非常好地解释了它,所以你应该阅读它。
如果需要,通用引用能够“衰减”成普通引用。