2017-09-20 1 views
9
#include <iostream> 
#include <algorithm> 
#include <vector> 

int main() 
{ 
    // Block 1 
    { 
    auto inc = []() { int i = 0; return [&]() { return i++; }; }(); 
    std::vector<int> v(10, 10); 
    std::generate(v.begin(), v.end(), inc); 
    for (auto i : v) std::cout << i << std::endl; 
    } 

    // Block 2 
    { 
    auto inc = []() { int i = 0; return [&]() { return i++; }; }; 
    std::vector<int> v(10, 10); 
    std::generate(v.begin(), v.end(), inc()); 
    for (auto i : v) std::cout << i << std::endl; 
    } 
} 

なぜこれら2つのブロックが異なる結果をもたらすのかわかりません。 ラムダを使用したC++クロージャ

Block 1: 32767 ... 32776 
Block 2: 0 ... 10 

std::generateジェネレータ(inc)値によって渡されたので、私はそれが正しい、問題ないはずと信じているため

私はOS Xのに

おかげで、


コード上記の結果は未定義であることに注意してください、以下を参照してくださいを実行しています。

+0

gcc v5.1.0はどちらの場合も同じ出力を生成します – Slava

+0

clangバージョン3.6.2でコンパイルしています。 – Ling

+1

それはUBなので、特に出力は無関係です – Slava

答えて

10

なぜこの2つのブロックが異なる結果をもたらすのかわかりません。

どちらも未定義の動作ですので、疑問があります。

auto f = []() { int i = 0; return [&]() { return i++; }; }; 

そしてf()戻りダングリング参照を持つラムダ:ifへのコールの終了時に破壊されたいずれの場合も、私たちは、ラムダなどを持っています。 generate()コールまたはgenerate()コールのかなり前に発生しているかどうかは、の場合はとなります。

あなたがラムダで生成カウンタを作りたい場合は、直接的な方法は、ラムダを可変にするとinit-キャプチャを使用することです:ラムダは、デフォルトではconstあるため

auto inc = [i=0]() mutable { return i++; }; 

mutableが必要とされ、メンバーiを直接変更する必要があります。


上記はC++ 14(一般的なinit-captureのため)です。私たちは、単に値でキャプチャを参照することにより、内側ラムダキャプチャから、ネストされたラムダ構造を変更することにより、C++ 11でこの作品を作ることができる:

auto inc = []{ int i = 0; return [=]() mutable { return i++; }; }(); 
//        ~~~ ~~~~~~~ 

...嫌なのようなものだが、それが動作することを?

+0

[i = 0]にはC++ 14が必要ですか? – Slava

+0

@ Slavaはい、OPの元のネストされたラムダの例をC++ 11でもそのまま使えるように修正する方法を追加しました。 – Barry

+0

感謝します。lambdaが 'struct'のように思えるなら、' i'は 'operator()'内部のローカルな一時的なオブジェクトです。したがって、返されたラムダがキャプチャするのは、ローカル変数への無効な参照です。そういうわけで、どちらのケースもUBです。最初は本当の閉鎖のように振る舞うだろうと思った。 – Ling

関連する問題