[英]Is this correct usage of C++ 'move' semantics?
今晚我一直在看一些我過去幾天一直在研究的代碼,並開始閱讀移動語義,特別是std :: move。 我有幾個問題要求專業人士確保我走正確的道路而不做任何愚蠢的假設!
首先:
1)最初,我的代碼有一個返回大向量的函數:
template<class T> class MyObject
{
public:
std::vector<T> doSomething() const;
{
std::vector<T> theVector;
// produce/work with a vector right here
return(theVector);
}; // eo doSomething
}; // eo class MyObject
鑒於“theVector”在這個和“扔掉”中是暫時的,我將該函數修改為:
std::vector<T>&& doSomething() const;
{
std::vector<T> theVector;
// produce/work with a vector right here
return(static_cast<std::vector<T>&&>(theVector));
}; // eo doSomething
它是否正確? 這樣做有什么陷阱嗎?
2)我注意到在一個函數中我返回了std::string
,它自動調用了移動構造函數。 調試返回字符串(thankyou,Aragorn),我注意到它稱為顯式移動構造函數。 為什么有一個字符串類而不是矢量?
我沒有必要對此函數進行任何修改以利用移動語義:
// below, no need for std::string&& return value?
std::string AnyConverter::toString(const boost::any& _val) const
{
string ret;
// convert here
return(ret); // No need for static_cast<std::string&&> ?
}; // eo toString
3)最后,我想做一些性能測試,是因為std :: move語義得到了驚人的快速結果,還是我的編譯器(VS2010)做了一些優化?
(為簡潔省略了_getMilliseconds()
實現)
std::vector<int> v;
for(int a(0); a < 1000000; ++a)
v.push_back(a);
std::vector<int> x;
for(int a(0); a < 1000000; ++a)
x.push_back(a);
int s1 = _getMilliseconds();
std::vector<int> v2 = v;
int s2 = _getMilliseconds();
std::vector<int> v3 = std::move(x);
int s3 = _getMilliseconds();
int result1 = s2 - s1;
int result2 = s3 - s2;
結果顯然非常棒。 result1,標准作業,耗時630ms。 第二個結果是0ms。 這是對這些事情的良好性能測試嗎?
我知道其中一些對你們很明顯很明顯,但我想確保在我對代碼進行開拓之前理解語義。
提前致謝!
參考仍然是參考。 以同樣的方式,你不能在C ++ 03中返回對本地的引用(或者你得到UB),你不能在C ++ 0x中。 你最終會得到一個死對象的引用; 它恰好是一個右值參考。 所以這是錯的:
std::vector<T>&& doSomething() const
{
std::vector<T> local;
return local; // oops
return std::move(local); // also oops
}
你應該做你在第二個看到的:
// okay, return by-value
std::vector<T> doSomething() const
{
std::vector<T> local;
return local; // exactly the same as:
return std::move(local); // move-construct value
}
返回時函數的局部變量是臨時的,因此無需更改任何代碼。 返回類型是負責實現移動語義的東西,而不是你。
你想使用std::move
來顯式移動一些東西,當它不能正常地完成時,就像你的測試一樣。 (這似乎很好;是Release中的那個嗎?你應該輸出向量的內容,否則編譯器會優化它。)
如果您想了解右值參考, 請閱讀此內容 。
return(theVector);
由於特殊的語言規則,這已經隱式移動,因為theVector
是一個本地對象。 見第12.8節第34和35段:
當滿足某些條件時,允許實現省略類對象的復制/移動構造,即使該對象的復制/移動構造函數和/或析構函數具有副作用。 在這種情況下,實現將省略的復制/移動操作的源和目標視為僅僅兩種不同的引用同一對象的方式,並且該對象的銷毀發生在兩個對象的后期時間。沒有優化就被破壞了。 復制/移動操作的省略,稱為復制省略,在以下情況下允許(可以合並以消除多個副本):
- 在具有類返回類型的函數的return語句中,當表達式是具有與函數返回類型相同的cv-unqualified類型的非易失性自動對象的名稱時,可以通過構造省略復制/移動操作自動對象直接進入函數的返回值
[...]
當滿足復制操作的省略標准並且要通過左值指定要復制的對象時,首先執行用於選擇復制的構造函數的重載決策,就好像該對象由右值指定一樣 。
請注意,您必須返回std::vector<T>
( 按值 ), 而不是 std::vector<T>&&
( 通過引用 )。
但為什么括號? return
不是函數:
return theVector;
要添加到GMan的答案:即使您將返回類型更改為std::vector<T>
(沒有任何引用,否則您將獲得UB),您在“1)”中的返回表達式的更改將永遠不會使性能更好,但可能會讓它更糟糕。 由於std::vector
有移動構造函數,並且你返回一個本地對象,所以不會調用vector
的復制構造函數,無論你寫了什么return theVector;
, return static_cast<std::vector<T>&&>(theVector);
,或return std::move(theVector)
。 在最后兩種情況下,編譯器將被強制調用移動構造函數。 但在第一種情況下,它可以自由地完全優化移動,如果它可以為該功能做NRVO。 如果由於某種原因無法使用NRVO,那么編譯器只會調用移動構造函數。 所以不要改變return x;
return std::move(x);
如果x
是從中返回的函數中的本地非靜態對象,否則您將阻止編譯器使用其他優化機會。
移動東西的標准方法是使用std::move(x)
,而不是static_cast
。 AFAIK,命名返回值優化可能會在按值返回向量時啟動,因此在移動語義之前它也會表現良好。
你的性能測試很好地說明了移動語義如何有利於性能:復制賦值必須復制一百萬個元素,移動賦值基本上只是交換向量的內部指針,這是一個簡單的單詞分配或兩個,只需幾個周期。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.