2013-10-18 47 views
-2

我一直在閱讀有關右值引用和std::move,我有一個性能問題。對於下面的代碼:應該在這裏使用右值引用嗎?

template<typename T> 
void DoSomething() 
{ 
    T x = foo(); 
    bar(std::move(x)); 
} 

它會更好,從性能的角度來看,要重寫這個使用右值引用,像這樣?

template<typename T> 
void DoSomething() 
{ 
    T&& x = foo(); 
    bar(x); 
} 

如果我理解正確的右值引用,這將只是充當如果bar(foo())已被調用,如評論者指出。但是可能需要中間值;這樣做有用嗎?

+1

可能不是。但是'foo'和'bar'是什麼樣的? – juanchopanza

+3

爲什麼不只是'bar(foo())'? –

+0

@MichaelBurr,我主要想知道它的理論性能。當然,'bar(foo())'會更乾淨,除非你需要將結果放在兩行之間。 – thirtythreeforty

回答

4

使用右值引用/移動語義的最佳方式是絕大部分,不要太費勁。按值返回函數當地人。和值從這些功能構建它們:

X foo(); 

template<typename T> 
void DoSomething() 
{ 
    T x = foo(); 
    ... 

如果你是class X作者,那麼你可能想確保X具有高效運行的建設和移動分配。如果拷貝構造函數和拷貝的X分配已經有效的,從字面上有什麼更多的事情要做:

class X 
{ 
    int data_; 
public: 
    // copy members just copy data_ 
}; 

如果你發現自己分配內存在X的拷貝構造函數,和或拷貝賦值,那麼你應該考慮編寫移動構造函數並移動賦值運算符。盡你所能讓他們成爲noexcept

儘管總有例外的規則,一般來說:

  1. ,除非你想在客戶端有一個非功能的直接訪問,不要通過引用返回,無論是左值或右值引用,本地變量。

  2. 不要通過引用,左值或右值捕獲返回值。是的,有些情況下它可以爲你節省一些東西,而不會讓你陷入困境。根本不要考慮這樣做。只有在你的性能測試高度激勵你這樣做的時候才做這樣的事情。你希望學習的所有東西都不用左值引用,但對右值引用(例如從函數返回一個懸掛引用)仍然是危險的。

  3. 正確性的第一個程序。這包括編寫廣泛和易於運行的單元測試,涵蓋API的常見和特殊情況。 然後開始優化。當你開始嘗試編寫非常優化的代碼(例如通過引用獲取返回值)之前,你有一個正確和經過良好測試的解決方案之前,不可避免地寫的代碼是如此脆弱,以至於第一個錯誤修復會進一步破壞代碼,但通常以非常微妙的方式。這是一個教訓,即使有經驗的程序員(包括我自己)也必須反覆學習。

  4. 儘管我在(4)中說了什麼,即使在第一次寫作時,也要留意O(N)的表現。即如果由於基本的整體設計缺陷或者算法不好或者算法不能在合理的時間內執行其基本功能而導致您的第一次嘗試非常緩慢,那麼您需要的不僅僅是新代碼,還需要一個新設計。在這個子彈中,我不是在談論你是否通過右值引用獲得了回報。我在談論你是否已經創建了O(N^2)算法,當O(N log N)或O(N)能夠完成這項工作時。當需要完成的工作不是微不足道的,或者代碼太複雜以至於無法確定發生了什麼時,這很容易實現。

1

這裏std :: move沒有幫助。
由於您製作了對象的副本(儘管elision可能有所幫助)。

template<typename T> 
void DoSomething() 
{ 
    T x = foo(); 
    bar(std::move(x)); 
} 

這裏右值引用是沒有幫助
作爲可變x是一個命名對象(並因此不是一個rvalue參考(再))。

template<typename T> 
void DoSomething() 
{ 
    T&& x = foo(); 
    bar(x); 
} 

最好使用:

template<typename T> 
void DoSomething() 
{ 
    bar(foo()); 
} 

但是如果你必須在本地使用它:我雖然不是100%地肯定上述

template<typename T> 
void DoSomething() 
{ 
    T&& x = foo(); 
    // Do stuff with x 
    bar(std::move(x)); 
} 

,他會喜歡一些反饋。

+1

讓我們假設'foo()'產生一個prvalue(即不是一個參考),例如, 'MyClass foo();'。然後,在第一個示例中:'T x = foo();'如果可能的話,不做任何事情(複製/移動elision),如果可能的話移動,或者複製(例如,如果有用戶提供的副本, )。 'bar(std :: move(x))'可以用'bar'按值取代時通過移動來替換副本。 'T && x = foo();'可能會阻止複製或移動,儘管這不太可能(複製/從臨時返回值移出,可以省略)。它也適用於不允許複製/移動的情況。 – dyp

+0

@DyP:所以如果可能的話,第一個版本會移動。這個'std :: move'來調用bar實際上很有用。 –

+0

是的,如果'bar'採用值並且'T'是可移動的,'std :: move(x)'在第一個例子中很有用(同樣,如果'bar'有一個右值ref超載,它可能會改變語義)。同樣,如果您在第二個示例中添加它(在與第一個示例中相同的條件和相同的原因下),它*可能會有所幫助。 – dyp