我有这个代码,它不编译,这是预期的.
这是错误:rvalue引用不能绑定到左值
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编译器中获得相同的行为.
解决方法
是.在模板函数的情况下,编译器推导出模板参数T,使其与给定的参数匹配.
由于someData实际上是一个左值,因此T被推导为SomeData& ;.然后,在类型推断之后,函数的声明变为
void Function(SomeData & &&)
和SomeData& &&,跟随rules for reference collapsing,成为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> ;.)