2011-12-01 51 views
18

爲什麼下面的代碼有效:爲什麼完美的轉發功能必須被模板化?

template<typename T1> 
void foo(T1 &&arg) { bar(std::forward<T1>(arg)); } 

std::string str = "Hello World"; 
foo(str); // Valid even though str is an lvalue 
foo(std::string("Hello World")); // Valid because literal is rvalue 

但不是:

void foo(std::string &&arg) { bar(std::forward<std::string>(arg)); } 

std::string str = "Hello World"; 
foo(str); // Invalid, str is not convertible to an rvalue 
foo(std::string("Hello World")); // Valid 

爲什麼不例子2中,左值獲得同樣的方式,它並實例1中得到解決?

此外,爲什麼標準認爲需要在std :: forward中提供參數類型而不是簡單地推導它呢?無論何種類型,簡單地向前轉就顯示意圖。

如果這不是一個標準的東西,只是我的編譯器,我使用msvc10,這將解釋蹩腳的C++ 11支持。

感謝

編輯1:更改文字的 「Hello World」 進行的std :: string( 「Hello World」 的),使右值。

+0

酒吧會發生什麼?編譯並不意味着它是必須的。我相信它應該分別爲'void foo(T1&arg)'和'void foo(std :: string&arg)'。 – AJG85

+1

「Hello World」不是一個右值,它是一個類型爲const char [12]的左值。 – GManNickG

+0

@ AJG85酒吧裏發生的事情並不重要。 &&表示右值引用。 – Mranz

回答

15

首先,read this得到轉發的完整想法。 (是的,我正在將這個答案的大部分委託給其他地方。)

總之,轉發意味着左值保持左值和右值不變。你不能用一種類型來完成,所以你需要兩個。因此,對於每個轉發的參數,您需要該參數的兩個版本,這需要函數的總數組合。你可以用代碼編寫該函數的所有組合,但是如果你使用模板,那麼根據需要爲你生成各種組合。


如果你想優化複製和移動,如:

struct foo 
{ 
    foo(const T& pX, const U& pY, const V& pZ) : 
    x(pX), 
    y(pY), 
    z(pZ) 
    {} 

    foo(T&& pX, const U& pY, const V& pZ) : 
    x(std::move(pX)), 
    y(pY), 
    z(pZ) 
    {} 

    // etc.? :(

    T x; 
    U y; 
    V z; 
}; 

那麼你應該停下來做這樣說:

struct foo 
{ 
    // these are either copy-constructed or move-constructed, 
    // but after that they're all yours to move to wherever 
    // (that is, either: copy->move, or move->move) 
    foo(T pX, U pY, V pZ) : 
    x(std::move(pX)), 
    y(std::move(pY)), 
    z(std::move(pZ)) 
    {} 

    T x; 
    U y; 
    V z; 
}; 

你只需要一個構造函數。 準則:如果您需要您自己的數據副本,請在參數列表中進行復制;這可以決定複製或移動到調用者和編譯器。

+1

所以第一個例子工作的原因是因爲T1實際上被解析爲等同於'void foo(const std :: string&&arg)',它被使用引用減少到void foo(const std :: string&arg)扣除規則?它在示例2中失敗,因爲該字符串沒有左值過載?是否有任何定義模板函數的最佳實踐,以至於它至少應該是相對明顯的類型?理想情況下,我正在尋找一種很好的方法來避免2^N重載,並明確使用該類型。 – Mranz

+1

@Mranz:正確。那麼,你要做什麼?轉發和模板是爲了用於轉發,你不應該真的需要知道類型。 – GManNickG

+0

可以說我有一個構造函數,它需要像'Class(.. some args ...,vector items)這樣的矢量'。理想情況下,我想允許這個參數避免使用移動語義或作爲右值的副本。爲了支持這一點,我要麼必須模板項目,要麼創建右值超載。如果我將其模板化,實際的類型將在定義中丟失,實際的參數將由我的課程的消費者通過閱讀頭文件或一些評論推演出來。 – Mranz