2017-08-06 65 views
15

我在玩std::variant, lambdasstd::future,當我試圖將它們組合在一起時,得到了超級奇怪的結果。下面舉例說明:無法初始化各種lambda表達式的std :: variant

using variant_t = std::variant< 
    std::function<std::future<void>(int)>, 
    std::function<void(int)> 
>; 
auto f1 = [](int) { return std::async([] { return 1; }); }; 
auto f2 = [](int) { return std::async([] { }); }; 

variant_t v1(std::move(f1)); // !!! why DOES this one compile when it SHOULDN'T? 
auto idx1 = v1.index(); //equals 1. WHY? 

variant_t v2(std::move(f2)); // !!! why DOESN'T this one compile when it SHOULD? 

以下是編譯錯誤:

Error C2665 'std::variant<std::function<std::future<void> (int)>,std::function<void (int)>>::variant': none of the 2 overloads could convert all the argument types

OK,從返回voidint讓改變variant的物品簽名:

using variant_t = std::variant< 
    std::function<std::future<int>(int)>, 
    std::function<int(int)> 
>; 

variant_t v1(std::move(f1)); // COMPILES (like it should) 
auto idx1 = v1.index(); // equals 0 

variant_t v2(std::move(f2)); // DOESN'T compile (like it should) 

這是什麼鬼去這裏?爲什麼std::future<void>如此特別?

+1

注意:'std :: variant'是一個C++ 17特性,而不是C++ 11。 – Rakete1111

+1

你的編譯錯誤來自第二個例子,但你的問題看起來像是從你的第一個例子。 – Barry

回答

16

variant的轉換構造函數模板採用重載解析來確定構造對象應該具有哪種類型。特別是,這意味着如果對這些類型的轉換同樣好,則構造函數不起作用;在你的情況下,如果你的論點中只有一個std::function專業化是可以構建的,它就可以工作。

那麼當function<...>從給定的參數可構造?從C++ 14開始,如果可以使用參數類型調用參數並生成類型爲convertible to the return type的類型。請注意,根據此規範,如果返回類型爲void,則任何事情都會發生(如any expression can be converted to void with static_cast)。如果你有function返回void,你傳入的仿函數可以返回任何東西—這是一個功能,而不是一個錯誤!這也是爲什麼function<void(int)>適用於f1。另一方面,future<int>不會轉換爲future<void>;因此,只有function<void(int)>是可行的,並且所述變體的索引爲1

然而,在第二種情況下,返回拉姆達future<void>,這可以轉換爲兩個future<void>void。如上所述,這導致兩個function專業化是可行的,這就是爲什麼variant無法決定構建哪一個。

最後,如果將返回類型調整爲int,則可以避免整個void轉換問題,因此一切都按預期工作。

+0

感謝您的回覆。但我仍然無法想象'std :: future '可以隱式轉換爲'void'(或'void'爲'std :: future ' - 我不確定我瞭解這一點)?這是否意味着什麼可以轉換爲「無效」? –

+3

@DmitryKatkevich這是正確的,任何表達式都可以通過'static_cast'轉換爲'void'。否則,我們無法向'function'提供函子,當'function'產生'void'時返回一些東西,這是一個理想的特性! – Columbo

相關問題