2017-09-20 11 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; 
    } 
} 

Nie jestem pewien, dlaczego te dwa bloki dają różne wyniki.C++ zamknięcie z lambda

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

I std::generate generator (inc) jest przekazywany przez wartość, więc wierzę powinno być w porządku, prawda?

Używam OS X.

Dzięki,


Należy zauważyć, że wyniki uzyskane powyżej kodu są niezdefiniowane, patrz poniżej.

+0

Nie można odtworzyć, gcc v5.1.0 generuje to samo wyjście w obu przypadkach – Slava

+0

Kompiluję z wersją 3.6.2. – Ling

+1

Nieważne, to UB, więc konkretne wyjście nie ma znaczenia – Slava

Odpowiedz

10

Nie jestem pewien, dlaczego te dwa bloki dają różne wyniki.

Obie są niezdefiniowane, więc pytanie jest dyskusyjne. W obu przypadkach mamy lambda, takich jak:

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

I f() Zwraca lambda, która ma dangling odniesienia: i jest zniszczone pod koniec wywołaniu f. To niematerialne , gdy pojawia się zwisające odwołanie - czy dzieje się to długo przed wywołaniem generate() lub w wywołaniu generate().

Jeśli chcesz, aby licznik wytwórczych z lambda, bezpośredni sposób to zrobić lambda zmienny i użyć init-capture:

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

mutable jest wymagane, ponieważ lambdas są const domyślnie i musimy bezpośrednio zmodyfikować członka i.


Powyższe jest w C++ 14 (ze względu na uogólniony init-capture). Mogliśmy zrobić tę pracę w C++ 11 poprzez zmianę struktury zagnieżdżone-lambda od wewnętrznej przechwytywania lambda przez odniesienie do przechwytywania przez wartość:

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

To ... niby nieznośny, ale to działa?

+0

Czy [i = 0] wymaga C++ 14? – Slava

+0

@Slava Tak, dodałem sposób naprawienia oryginalnego przykładu zagnieżdżonego kodu źródłowego OP, aby nadal działał w C++ 11. – Barry

+0

Dzięki, ponieważ 'i' jest lokalnym tymczasowym obiektem wewnątrz' operator() 'jeśli uważasz, że lambda przypomina' struct'. Tak więc to, co zwraca zwrócony lambda, jest nieprawidłowym odniesieniem do zmiennej lokalnej. Dlatego oba przypadki to UB. Na początku myślałem, że zachowa się jak prawdziwe zamknięcia. – Ling