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
がコピーによってキャプチャしたようですinit
1回のみ。しかし、なぜ?このように動作するはずですか?
今、私が理解しているように、ラムダは、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
への参照がぶら下がっていて、それを使用すると未定義の動作になります。
ラムダは、コンパイラが生成する構造体であり、次のものと同等です。
struct lambda
{
int init = 0; // captured value
auto operator()() // non-const, due to `mutable`
{
return ++init;
}
};
したがって、init
は、ラムダ内で1回だけキャプチャおよびコピーされます。ラムダを複数回呼び出しても、init
は再度キャプチャされません。
あなたは対処して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;
あなたのエラーはここにあります「今、ラムダは呼び出されます各ベクトルのアイテムについてと呼ばれます。番号;ご覧のとおり、ラムダは完全に分離されているため、ベクトルコードとは無関係です。 item
の初期化は、ラムダフォーム自体が評価されるたびに行われます(結果の値が呼び出されるたびとは異なります)。ここでは、これは、関数generate
が呼び出されるたびに1回だけであることを意味します。