C++有右值引用以後是否可以直接return 字元串、結構體而無需考慮大量數據複製的性能問題了?
如果返回的字元串非常大有關係嗎(我知道大字元串會在堆中分配,但不知道棧中要佔多少內存,以及處理要多久)?
如果是一個比較大的結構體有關係嗎?
感覺這種直接return的寫法好爽(尤其是處理字元串的時候),就是怕性能問題。
java,c#等其他語言對象都是個指針,不存在這個問題。
返回臨時對象、或者返回std::move(已經不會被繼續使用的左值引用)是沒有問題的。
P.S.
必須指出,其他答案說的RVO跟這個東西的情況不是太一樣,因為那些東西只能處理返回臨時對象的那前面的一半,後面的一半沒有std::move還是對付不了。
std::mt19937,整個C++標準庫里最大的類,4004位元組,想想也不能直接返回。
其他的差不多都行,心虛的話跑個benchmark,別腦補性能瓶頸首先RVO是有條件的。
X a(1), b(2);
if(blabalbal){
return a;
}
if(balbbbb){
return b;
}
return a;
這種有多路返回分支,並且返回的不是右值,而且blabla裡面可能對a,b的內容有改變,是不能做RVO的,你沒法保證有一天你的代碼不會被改成這個樣子。
其次,右值拷貝依然是拷貝,只是相對來說拷貝的可能會輕鬆一點,可以利用pimpl+交換之類的方法快速掏空右值。但是對對象結構是要求的,不是每個類都可以實現右值拷貝比左值快的。
void WriteString(string result)
void WriteBigStruct(BigStruct result)
一直就有RVO和NRVO,17之後Copy elision變成強制,17之前像話的編譯器也都會做。只要函數里用對了就行(比如說,不要在函數的不同路徑里返回不同的具名變數,那就做不了NRVO了)。
具體可以看這個:
Copy elision - cppreference.com
通常你應該盡量做到讓編譯器來完成copy elision,而不是依賴std::move來迴避複製,因為移動構造的代價不一定真的很小(比如std::array的移動複製代價就是O(N)而不是O(1))。
準確來說需要的不僅是右值引用而是移動構造函數,還要類型本身適合移動。返回一個龐大的 POD 類對象也是不太舒服的。另外也應該考慮下能否利用 RVO/NRVO ,C++17 中 RVO 變為強制,而且允許返回類型沒有複製和移動構造函數。
如果需要返回大數據結構對象可以考慮NRV優化,右值引用配合std::move對大字元串確實有效,不過自己定義的類要想利用右值引用和move就要在數據數據時考慮pimpl。
沒有右值引用的時候也可以啊,編譯器有優化的,copy elision
再大的結構都可以直接返回,不會拷貝,類似emplace
右值引用只是個修飾符,和cv一樣,用來表示變數是臨時量。
具體使用還得需要程序員手工重載對應的函數。
而且右值比rvo優化,多了一次右值拷貝,一次析構函數。其他答案已經說了複製消除(copy elision),我就講一下基本原理:
首先機器層面返回不了結構,都是隱式傳指針進去寫,
然後,不管你是「 t foo; return foo;」還是直接「 return foo();」,都會直接從傳進去的那個指針來構造,
最後,如果你還不信,可以動手試驗一下,你會發現並沒有調用拷貝或是移動構造函數。
推薦閱讀:
※C++ 研發實習生面試通常會被問到什麼問題?
※怎麼返回容器中部分內容的引用?
※C++中將一個成員函數定義為const的會有助於提高性能嗎?
※C++LNK2019錯誤如何解決?
TAG:C |