2014-04-25 54 views
10

據我所知,在C++ 11中,通用引用應該始終與std::forward一起使用,但我不確定如果不使用std::forward會出現什麼樣的問題。未轉發通用引用會出現什麼樣的問題?

template <T> 
void f(T&& x); 
{ 
    // What if x is used without std::forward<T>(x) ? 
} 

您能提供一些在這種情況下可能發生的問題的插圖嗎?

回答

16

沒有這樣的規則總是使用std::forward普遍引用。相反,在通用參考文獻的功能中使用std::forward可能是危險的。看看下面的例子:

template <typename T> 
auto make_pair(T&& t) 
{ 
    return std::make_tuple(std::forward<T>(t), std::forward<T>(t)); // BAD 
} 

如果調用此函數make_pair(std::string{"foobar"}),結果是反直覺的,因爲你從同一對象移動兩次。


更新:下面是另一個例子表明,它真的很有意義的使用普遍引用完美轉發

template <typename Range, typename Action> 
void foreach(Range&& range, Action&& action) 
{ 
    using std::begin; 
    using std::end; 
    for (auto p = begin(range), q = end(range); p != q; ++p) { 
     action(*p); 
    } 
} 
  • 這是很好的範圍通用參考,以便呼叫者可以使用foreach用一個臨時容器和一個動作,這就是在元素上調用一個非const的成員函數
  • 這是很好的行動通用參考,使主叫方可以通過一個可變lambda表達式爲行動。
  • 並且使用std::forward代替範圍或代替動作將是錯誤的。
+1

非常好的一點,但它並沒有明確回答「如果不使用std :: forward會出現什麼樣的問題?」的問題。 – Nicolas

+0

+1,但爲什麼將'std :: forward'用於'action'會出錯?我只是讀了這個:[爲什麼使用一個完美轉發的值?](http://stackoverflow.com/q/24779910/873025),並且如果不保留r值,你似乎可能丟掉一個優化機會。 – Snps

+0

@Snps:與'std :: apply'相反,上面的代碼包含一個循環。根據'Action'的類型,'std :: forward (action)'可以與'std :: move(action)'相同。這意味着對於'std :: forward',你可能會在第一次迭代中更改函數對象'action',並導致所有剩餘迭代的奇怪行爲。 – nosid

9

比方說f被稱爲像這樣:

f(someType{}); 

如果f身體上執行x

foo(x); 

某種運算並有兩個重載foo

void foo(someType const&); 

void foo(someType&&); 

不使用std::forward,作爲x是一個左值以下過載被稱爲

void foo(someType const&); 

這可能會花費你一個潛在的優化。

使用

foo(std::forward<T>(x)); 

確保的foo正確超載被選中。

5

帶名稱的東西是左值。這意味着在函數體中,t是一個左值。如果通用參考結束爲左值參考或右值參考,則無關緊要。如果它被命名,它是一個左值。

如果您因此直接傳遞該參數,您將傳遞一個左值。

在您只想將不透明參數傳遞給另一個函數的情況下,您想要按原樣傳遞它。如果它是一個左值,你想把它作爲一個左值;如果它是一個右值你想傳遞一個右值。正如所解釋的,直接傳遞它作爲左值總是forward是否需要通過它作爲正確的價值,

總是通過一個左值的代碼可能會受性能的影響,但我會說,當你考慮這個特定的問題時你可以忽略它。有一個更迫切的問題,並不總是通過一個左值:雖然複製某些類型可能很昂貴,但其他類型不能複製的所有,如std::unique_ptr。對於那些保留右值的類型,當轉發不是性能問題,而是讓你的代碼進行編譯時。

0

您不應多次轉發變量。

相關問題