web-dev-qa-db-ja.com

C ++でラムダにパラメーターを渡す

C++のラムダメカニズムのいくつかのポイントを見逃しているようです。これがコードです:

std::vector<int> vec (5);

int init = 0;
std::generate(begin(vec), end(vec), [init]() mutable { return ++init; });

for (auto item : vec) {
    std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;

mutableがない場合は、ラムダでinitを変更しているため、コンパイルされません。
さて、私が理解しているように、ラムダは各ベクトルのアイテムに対してnew fresh copy of initで呼び出されます。したがって、毎回1​​を返す必要があります。しかし、このコードの出力は次のとおりです。
1 2 3 4 5

実行の最初は、generateがコピーによってキャプチャしたようですinit1回のみ。しかし、なぜ?このように動作するはずですか?

15
IgorStack

今、私が理解しているように、ラムダは、0であるinitの新しい新鮮なコピーで各ベクトルのアイテムに対して呼び出されます。

不正解です。ラムダは、クラスを作成してoperator()を提供するもう1つの方法です。ラムダの_[]_の部分は、メンバー変数と、それらが参照によって取得されるか値によって取得されるかを示します。ラムダの_()_部分はoperator()のパラメーターリストであり、_{}_部分はその関数の本体です。 mutableの部分は、operator()をデフォルトでconstと同じようにconstにしないようにコンパイラーに指示します。

そう

_[init]() mutable { return ++init; }
_

なる

_struct compiler_generated_name
{
    int init; // we captured by value

    auto operator()() // since we used mutable this is non const
    {
        return ++init;
    }
};
_

タイプを簡潔にするためにここでは構造体を使用しましたが、クラスタイプとしてラムダが指定されているため、classを使用できます。

つまり、initは、最後の反復から取得したinitは、一度しかキャプチャしないので、同じです。これは覚えておくことが重要です

_auto generate_lambda()
{
    int foo = 0;
    return [&foo](){ return ++foo; };
}
_

関数が戻ったときにfooへの参照がぶら下がっていて、それを使用すると未定義の動作になります。

24
NathanOliver

ラムダは、コンパイラが生成する構造体であり、次のものと同等です。

struct lambda
{
    int init = 0; // captured value

    auto operator()() // non-const, due to `mutable`
    {
        return ++init;
    }
};

したがって、initは、ラムダ内で1回だけキャプチャおよびコピーされます。ラムダを複数回呼び出しても、initは再度キャプチャされません。

10
Vittorio Romeo

あなたは対処してinitの初期値を見ています-あなたが期待することに従っておそらくあなたがしたいことは参照によってinitをキャプチャすることです...

std::vector<int> vec (5);

int init = 0;
std::generate(begin(vec), end(vec), [&init]() mutable { return ++init; });

for (auto item : vec) {
    std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
4
Soren

あなたのエラーはここにあります「今、ラムダは呼び出されます各ベクトルのアイテムについてと呼ばれます。番号;ご覧のとおり、ラムダは完全に分離されているため、ベクトルコードとは無関係です。 itemの初期化は、ラムダフォーム自体が評価されるたびに行われます(結果の値が呼び出されるたびとは異なります)。ここでは、これは、関数generateが呼び出されるたびに1回だけであることを意味します。

3