2014-01-20 36 views
18

時,順便推導型的R值參考我得到普遍的參考功能,可archieve完美轉發這樣的:捕捉通用參考

template <typename T> 
void func(T&& t) { 
    other_func(std::forward<T>(t)); 
} 

...由於T的衍生方式和標準的參考摺疊規則。

現在考慮other_func需要一個函數對象

template <typename T> 
void func(T&& t) { 
    other_func([](int v) { return t + v; }); // I chose addition for example purposes 
} 

現在很明顯這不會編譯由於到t沒有被捕獲。我的問題是:我如何捕獲它,以便捕獲的值將是推導出的T?

這可能使用新的通用lambda捕獲?如果...如何?

[t = std::forward<T>(t)] ? 

我仍然沒有真正得到新的捕獲初始化的力學...

+0

快速注:目前公認的名稱轉發參考,不具有普遍性參考 –

回答

5

您可以「捕獲由通用參考」,在C++ 11中,由於模板參數T的類型是提供給lambda函數(hideous live code example at Coliru):

template <typename T> 
void func(T&& t) { 
    other_func([&t](int v) { 
    return std::forward<T>(t) + v; 
    }); 
} 
+0

嗯,這很有趣 - 認爲lambda中t的類型會摺疊爲l值引用。另一個說明:在運算符聲明之後,你寫的引用說明符的名稱是什麼?不知道這些存在 –

+0

@RichardVock這些是[ref-qualifiers](http://stackoverflow.com/questions/17521238/what-are-rvalue-references-for-this-for)。與成員函數上的const限定符非常相似,它們約束可能調用該函數的對象。例如,當你在右值對象上調用'foo'時,調用'void foo()&&'。 – Casey

+4

一個可能的問題:如果'other_func'存儲了這個lambda,並且它的持續時間超過'func'的主體範圍,那麼調用lambda就是UB,因爲捕獲的變量't'已經離開了範圍。 – Yakk

6

好吧,讓我們試試這個。不幸的是,我沒有一個支持這個功能的編譯器,所以如果我嚴重誤解了這些東西,請原諒我。

涵蓋此的提議是N3648

這裏有趣的是,如同使用auto,在初始化捕獲的變量的類型推導:

的類型構件的對應於所述形式的一個假想 變量聲明的類型「自動初始化捕獲」 [...]。

所以你從捕獲清單[c = std::forward<T>(t)]得到的問題相當於你從聲明auto c = std::forward<T>(t)得到的結果。

此處推導的類型將是std::remove_reference<T>::type(參考限定符將被刪除auto),因此您將始終在此處獲得新值。如果t是一個右值引用,您將移動構建該新值,否則您將複製構建(由於返回值爲std::forward)。

好處是這個新值由lambda擁有。因此,無論您最初通過何種t,都可以從c安全地獲取std::move。所以即使你不知道最初的t的類型,你仍然沒有失去任何東西。

+0

我想你需要一個包裝類型來支持完美的轉發。右值引用成員將不允許複製,而移動構造函數不需要用於lambda表達式。 – dyp

+0

@dyp是的,包裝類型將工作。但是,它也會很醜陋(任何人都可以使用'std :: auto_ptr_ref')。有趣的是,我認爲我們在這裏犧牲完美的轉發並不會帶來任何實質性的損失:如果我們傳入右值和副本,我們仍然只能採取行動。 – ComicSansMS

+1

的確如此,但舉動也可能很昂貴,例如當一個成員是僅複製(遺留數據類型,數組,...)時。我想到了一些像'template wrapper {T m; };模板自動換行(T && p) - >包裝 {return {std :: forward (p)}; }'然後'[t = wrap(std :: forward (t))]') – dyp

2

爲了實現所期望的行爲通過參考捕捉的C++ 14沒有通用拉姆達捕獲要求(但始終與參比捕獲,需要注意不要創建一個懸空參考):

template <typename T> 
void func(T&& t) { 
    other_func([&t](int v) { return std::forward<T>(t) + v; }); 
} 

相反,如果決定是使用c按值apture,拉姆達應標記爲可變的,以允許有效的運動(因爲const修飾詞並默認添加到lambda表達式):

template <typename T> 
void func(T&& t) { 
    other_func([t = std::forward<T>(t)](int v) mutable { return std::move(t) + v; }); 
} 
+0

在上面的例子中的lambda不會被值捕獲 - 因此在那裏不需要可變(實際上是避免mutable和C++ 14通用lambda捕獲的好方法) – user1115339

+0

哦,我錯過了。你是對的。 – dyp