[英]C++ why does passing an lvalue to a move constructor work for templates?
我有這個代碼,它不編譯,這是預期的。
這是錯誤: 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編譯器中獲得相同的行為。
是。 在模板函數的情況下,編譯器推導出模板參數T
,使其與給定的參數匹配。
由於someData
實際上是一個左值,因此T
被推導為SomeData &
。 然后,在類型推斷之后, Function
的聲明變為
void Function(SomeData & &&)
和SomeData & &&
,遵循參考折疊規則 ,成為SomeData &
。
因此,函數參數someData
變為左值引用,並且這樣傳遞給localData
的初始化。 注意(正如@aschepler正確指出的那樣) localData
被聲明為T
,所以它本身就是SomeData &
類型的引用。 因此,這里沒有復制構造 - 只是初始化引用。
如果您希望localData
是實際副本,則必須將類型從SomeData &
轉換為SomeData
,即您必須從類型中刪除&
。 你可以使用std::remove_reference
來做到這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>
。)
這確實是預期的行為。 表單的模板函數:
template< class T >
Ret Function( T&& param )
{
...
}
遵循特殊規則(Ret可以是或不是模板,沒關系)。 T &&被稱為通用引用,它基本上可以綁定到任何東西。 這是因為當模板推導開始並且param采用該形式時(請注意, vector<T>&&
不是通用引用,對於任何其他模板參數也不是C<T>
),應用引用折疊規則:
T = int&=> T && = int &&&並且折疊為單個int&
完整的表格是:
& + && = &
&& + & = &
&& + && = &&
所以當你有上述功能時
int a = 5;
int b& = a;
Function( a ); // (1)
Function( b ); // (2)
Function( 3 ); // (3)
在情況1中,T = int&且推導出的類型是int&(因為a是左值)因此Function具有以下簽名:
Ret Function( int& param ) // int& && collapses to int&
在案例2中,T = int&
Ret Function( int& param ) // int& && collapses to int&
在案例3中,T = int
Ret Function( int&& param )
這種崩潰規則是委員會認為合理的完美轉發工作。 你可以在Scott Meyers的視頻中找到長篇故事
只是為了讓你放心:這不是MSVC問題,這實際上是預期的行為。
在模板中, &&
在應用於模板類型參數時具有不同的含義。 它被稱為通用參考 。
我可以解釋一下,但是這篇文章很好地解釋了它,所以你應該閱讀它。
為了總結(並且非常不精確),通用引用能夠在需要時“衰減”成普通引用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.