C ++ 11で(同じタイプの)ラムダのベクトルを作成できないのはなぜですか?


88

ラムダのベクトルを作成しようとしましたが、失敗しました。

auto ignore = [&]() { return 10; };  //1
std::vector<decltype(ignore)> v;     //2
v.push_back([&]() { return 100; });  //3

2行目までは、正常コンパイルされます。しかし、3行目はコンパイルエラーを出します

エラー: 'std :: vector <main():: <lambda()>> :: push_back(main():: <lambda()>)'の呼び出しに一致する関数がありません

関数ポインタのベクトルや関数オブジェクトのベクトルは必要ありません。ただし、実際のラムダ式をカプセル化する関数オブジェクトのベクトルは、私にとっては機能します。これは可能ですか?


23
「関数ポインタのベクトルや関数オブジェクトのベクトルは必要ありません。」しかし、それはあなたが求めたものです。ラムダ関数オブジェクトです。
ニコルボーラス2011

回答:


135

すべてのラムダには、同じ署名がある場合でも、異なるタイプがあります。std::functionそのようなことをしたい場合など、実行時のカプセル化コンテナを使用する必要があります。

例えば:

std::vector<std::function<int()>> functors;
functors.push_back([&] { return 100; });
functors.push_back([&] { return  10; });

52
100人の開発者チームを管理することは、私にとっては悪夢のように聞こえます:)
Jeremy Friesner 2011

10
また、キャプチャレスラムダ([]スタイル)は関数ポインタに分解される可能性があることを忘れないでください。したがって、彼は同じ型の関数ポインタの配列を格納できました。VC10はまだそれを実装していないことに注意してください。
ニコルボーラス2011

ちなみに、これらの例では、キャプチャレスを使用するべきではありませんか?それとも必要ですか?-ちなみに、キャプチャレスラムダから関数ポインタはVC11でサポートされているようです。しかし、それをテストしませんでした。
クライム2011

2
異なるタイプの関数を格納するベクトルを作成することは可能ですか?つまり、に制限する代わりに、std::function<int()別の関数プロトタイプを使用できますか?
manatttta 2015年

2
@manattttaポイントは何でしょうか?コンテナは、同じタイプのオブジェクトを格納し、それらをまとめて操作するために存在します。「とのvector両方std::functionを格納するストレージを作成できstd::stringますか?」そして答えは同じです:いいえ、それは意図された使用法ではないからです。'variant'スタイルのクラスを使用して、異なるものをコンテナーに入れるのに十分な型消去を実行し、ユーザーが 'real'型を判別して、処理方法(呼び出し方法など)を選択するためのメソッドを含めることができます。各要素...しかし、繰り返しますが、なぜそのような長さに行くのですか?本当の理由はありますか?
underscore_d

40

すべてのラムダ式は、文字ごとに同一あっても、タイプが異なります。異なるタイプのラムダを(別の式であるため)ベクトルにプッシュしていますが、それは明らかに機能しません。

1つの解決策は、std::function<int()>代わりにのベクトルを作成することです。

auto ignore = [&]() { return 10; };
std::vector<std::function<int()>> v;
v.push_back(ignore);
v.push_back([&]() { return 100; });

別の注意点として、[&]何もキャプチャしていないときに使用することはお勧めできません。


14
()引数をとらないラムダは必要ありません。
子犬

18

他の人が言ったことは関連性がありますが、ラムダのベクトルを宣言して使用することは可能ですが、あまり有用ではありません。

auto lambda = [] { return 10; };
std::vector<decltype(lambda)> vec;
vec.push_back(lambda);

したがって、lambda!のコピー/移動である限り、そこにラムダをいくつでも格納できます。


これは、プッシュバックが異なるパラメーターを持つループで発生する場合に実際に役立つ可能性があります。おそらく遅延評価の目的で。
MaHuJa 2011

7
いいえ、パラメータをベクトルに配置するのではなく、関数オブジェクトのみを配置します。したがって、同じラムダのすべてのコピーを含むベクトルになります
hariseldon78 2012年

16

ラムダがステートレスの場合、つまり、[](...){...}C ++ 11では、ラムダを関数ポインターに分解できます。理論的には、C ++ 11準拠のコンパイラはこれをコンパイルできます。

auto ignore = []() { return 10; };  //1 note misssing & in []!
std::vector<int (*)()> v;     //2
v.push_back([]() { return 100; });  //3

4
記録のauto ignore = *[] { return 10; };ためignoreint(*)()
Luc Danton 2011

1
@Luc、ああ、それはグロスです!彼らはいつそれを追加しましたか?
MSN

3
そもそも関数ポインタをとることができる変換関数はそうではないことが義務付けられているのでexplicit、ラムダ式の逆参照は有効であり、変換の結果のポインタを逆参照します。次に、autoその参照をポインタに戻す減衰を使用します。(参照を使用するauto&auto&&、保持していたはずです。)
Luc Danton 2011

ああ...結果のポインタを逆参照します。それは理にかなっている。行方不明は()意図的または偶発的でしたか?
MSN

意図的に、ラムダ式は同等です(ただし、2文字短くなります)。
Luc Danton

6

ラムダ生成関数を使用できます(Nawazによって提案された修正で更新されました):

#include <vector>
#include <iostream>

int main() {
    auto lambda_gen = [] (int i) {return [i](int x){ return i*x;};} ;

    using my_lambda = decltype(lambda_gen(1));

    std::vector<my_lambda> vec;

    for(int i = 0; i < 10; i++) vec.push_back(lambda_gen(i));

    int i = 0;

    for (auto& lambda : vec){
        std::cout << lambda(i) << std::endl;
        i++;
    }
}

でも、基本的にはこの時点で自分でクラスを作ったと思います。それ以外の場合、ラムダのcaputres / argsなどが完全に異なる場合は、おそらくタプルを使用する必要があります。


それをlambda_genラムダ自体にすることができるような関数でラップするのは良い考えです。ただし、auto a = lambda_gen(1);不要な呼び出しを行うため、これを記述すれば回避できますdecltype(lambda_gen(1))
nawaz 2016

それでもまだ余分な電話をかけませんか?また、もう1つのマイナーな点は、質問にC ++ 11と記載されているため、関数の末尾に戻り値の型を追加する必要があると思います。
antediluvian 2016

いいえ。内部decltype評価されていないため、実際に電話をかけることはありません。も同じsizeofです。また、このコードは、末尾の戻り値の型を追加してもC ++ 11では機能しません!!
nawaz 2016

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.