2017-08-06 12 views
15

私はstd::variant, lambdasstd::futureで遊んでいて、それらを一緒に合成しようとしたときに非常に奇妙な結果を得ました。ここでの例は以下のとおりです。さまざまなラムダ式を使ってstd :: variantを初期化できません

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、intvoidを返すから変更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) 

ここ

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? 

は、コンパイルエラーがありますここにいる? std::future<void>はなぜ特別なのですか?

+1

注: 'std :: variant'はC++ 17の機能であり、C++ 11ではありません。 – Rakete1111

+1

あなたのコンパイルエラーは2番目の例のものですが、あなたの質問はあなたの最初のもののように見えます。 – Barry

答えて

16

variantのコンバーターコンストラクターテンプレートでは、オーバーロードの解像度を使用して、構築されたオブジェクトにどのタイプを割り当てるべきかを判断します。特に、これは、これらの型への変換が同等に良好であれば、コンストラクタが機能しないことを意味します。あなたのケースでは、std::functionの特殊化のうちの1つが、あなたの議論から構築可能であれば動作します。

したがって、与えられた引数からfunction<...>が構築可能なのはいつですか? C++ 14では、引数がパラメータ型で呼び出すことができ、型がconvertible to the return typeである場合。この仕様によれば、戻り値の型がvoidであれば、何も行われません(any expression can be converted to void with static_cast)。 functionvoidを返す場合、渡すファンクタは何でも返すことができます。それはバグではなく、機能である—です!これはf1にもfunction<void(int)>が適用される理由です。一方、future<int>future<void>に変換されません。従ってのみfunction<void(int)>生存可能であり、そして変異体のインデックスは、第二の場合に、ラムダは、future<void>void両方に変換されfuture<void>を返し、ただし1

あります。上記のように、これによりfunctionのスペシャライゼーションが実行可能になります。そのため、variantはどちらを構築するかを決定できません。

最後に、戻り値の型をintに調整すると、この完全なvoid変換の問題は回避されるため、すべて正常に動作します。

+0

ご返信ありがとうございます。しかし、私はまだ 'std :: future 'が暗黙的に 'void'(あるいは' void :: 'std :: future' '' '' '' '私はこの点を理解していません)何が起こっているのかを意味するのでしょうか? –

+3

@DmitryKatkevich正しいですが、どの式も 'static_cast'で' void'に変換できます。そうでなければ、 'function'が' void'を返すときに何かを返す 'function'にファンクターを与えることができませんでした。 – Columbo

関連する問題