2016-02-11 76 views
4

我想知道使用完美轉發仿函數的正確方法是什麼?這裏有兩個代碼片段。哪一個是最好的,如果兩者都不是,最好的形式是什麼?完美轉發仿函數

template<typename T, typename... Args> 
void callMe(T&& func, Args&&... args) { 
    func(std::forward<Args>(args)...); 
} 

或者

template<typename T, typename... Args> 
void callMe(T&& func, Args&&... args) { 
    std::forward<T>(func)(std::forward<Args>(args)...); 
} 

編輯:

它會影響重載解析?如果funcoperator()具有&&const &的ref-qualifier,我應該執行後一個版本,並且應該關心我打電話給哪個超載?

謝謝!

+0

後者是正確的,如果函數調用操作符可能有一個ref-qualifier –

+0

@PiotrSkotnicki,你能舉一個你的意思嗎? –

+1

@JohanLundberg http://coliru.stacked-crooked.com/a/2114bc29d189fde9 –

回答

4

由於裁判資格operator()存在,第一個版本可以做錯誤的事情。試想一下:

struct C { 
    void operator()() && { std::cout << "rval\n"; } 
    void operator()() const & { std::cout << "lval\n"; } 
}; 

callMe(C{}); 

我給你一個右值 - 並希望能看到"rval" - 但在第一個版本,你總是處理功能的物體,像一個左 - 所以我真的看到"lval"

所以正確的解決方案將是第二個 - 其中forward s func以及。


在實踐中,我不知道裁判資格的成員函數實際上是如何經常發生,因此前者可能是罰款。

+1

很酷。我不知道是否存在資格認證。 –

+0

我所擁有的恐懼是lambda的優化,其中右值lambda開始隱式地從其存儲的內部狀態(如本地變量)移動。在某些情況下,這可能會給你一些不錯的性能提升,而缺乏前鋒的則會丟失。但可能不會。 – Yakk

+0

@Yakk我讀到,因爲「它可能不可能正確編寫C++中的通用代碼」 – Barry

6

完美的轉發是有用的情況下,你要提供一個對象(可能是一個右值)的一些其他函數,你不知道它是一個右值還是左值。在這種情況下,您只需使用func,因此在轉發時沒有收益或傷害。

請記住,std::forward是一個有條件的std::move,它只是一個轉換爲r值參考。

另外,它只有在可能爲您轉發的對象調用複製或移動構造函數時纔有用。在很多情況下,const T&將會很好。

編輯:

漿果指出,它確實不管func有裁判資格operator()。我從來沒有見過或使用過符合資格的方法(據我所知,這非常罕見)。Barry的回答更多。

它會同時閱讀:What is "rvalue reference for *this"?

struct C { 
    void operator()() && { std::cout << "rval\n"; } 
    void operator()() const & { std::cout << "lval\n"; } 
}; 

callMe(C{}); 
+0

+1同意。對於其他讀者,我想指出,如果你只是要使用它,轉發它沒有什麼壞處,因爲std :: forward只是一個類型轉換爲r值引用('&&')或l值參考('&')。 – AndyG

+0

不同意「獲益/傷害」。如果'T'具有ref-qualified'operator()'會怎麼樣? – Barry