2013-04-17 11 views
8
#include <iostream> 

void foo(int k) { 
    static auto bar = [&]{ 
     std::cout << k << std::endl; 
    }; 
    bar(); 
} 

int main() { 
    foo(1); foo(2); foo(3); // output is correct: 1, 2, 3 
} 

Sprawdź funkcja foo, jak statyczne lambda jest przechwytywanie k przez odniesienie. To wydaje się działać, i to samo dzieje się z bardziej skomplikowanymi typami danych niż int.referencyjny ujęte w definicji zmiennej statycznej

Czy to jest oczekiwane? Czy istnieje gwarancja, że ​​adres k będzie taki sam dla każdego wywołania foo, czy jest to UB?

Z góry dziękuję i przepraszam, jeśli to było wcześniej odpowiedział (I nie spróbować znaleźć podobne pytanie bez powodzenia)

+0

Adam straciłeś punkt milowy, obawiam się – sehe

Odpowiedz

4

To jest niezdefiniowany Zachowanie.

za § 5.2.2/4 C++ 11 standardowych określeń o wywołaniu funkcji i inicjalizacji ich parametrów:

[...] Żywotność parametru kończy się wtedy, gdy funkcja w którym zdefiniowano , zwraca. Inicjowanie i niszczenie każdego parametru występuje w kontekście wywołującej funkcji . [...]

W związku z tym, twoja lambda będzie przechowywać referencję, która zwisa, gdy tylko wywołanie funkcji powróci.

W takim przypadku implementacje są bezpłatne (i prawdopodobnie) tworzą parametry funkcji pod tym samym adresem dla każdego wywołania funkcji, co prawdopodobnie jest przyczyną oczekiwanego wyniku.

Jednak takie zachowanie nie jest wymagane przez Standard - dlatego nie powinieneś polegać na nim (w takim przypadku Twój kod byłby legalny z powodu 3.8/7).

+0

Świetna odpowiedź! Co mówi 3.8/7? –

+1

@ JoséManuel: "* Jeśli po zakończeniu okresu istnienia obiektu i przed magazynem, w którym zajęty obiekt został ponownie wykorzystany lub zwolniony zostanie , nowy obiekt zostanie utworzony w miejscu przechowywania, w którym znajdował się pierwotny obiekt, wskazaniu, które wskazał do oryginalnego obiektu, odniesienie, które odnosiło się do oryginalnego obiektu, lub nazwa oryginalnego obiektu będzie automatycznie odnosić się do nowego obiektu i, po uruchomieniu okresu istnienia nowego obiektu, można użyć do manipulowania nowym obiektem , jeśli * [...] "- I oto warunki, które są spełnione przez twój przykład. –

+0

* "Wdrożenia są bezpłatne (i najprawdopodobniej) do tworzenia parametrów funkcji pod tym samym adresem dla każdego wywołania funkcji" * - Oczywiście, ale niezupełnie * "prawdopodobnie" *, biorąc pod uwagę, że zwykle będą przekazywane na stosie (którego górny adres jest bardzo * mało prawdopodobny * być taki sam za każdym razem, gdy funkcja jest wywoływana). A może miałeś na myśli w jego przykładzie, gdzie rzeczywiście * "prawdopodobnie" *, że wskaźnik stosu nie zmieni się między poszczególnymi wywołaniami funkcji? –

1

Powodem, dla którego prawdopodobnie "działa" w twoim przykładzie, jest to, że stos wywołań zawsze układa się w ten sam sposób. Spróbuj tego zamiast tego i sprawdź, czy nadal masz "oczekiwane" dane wyjściowe.

#include <iostream> 

void foo(int k) { 
    static auto bar = [&]{ 
     std::cout << k << std::endl; 
    }; 
    bar(); 
} 

void baz(int k) { 
    std::cout << "baz: "; 
    foo(k); 
} 

int main() { 
    foo(1); baz(2); foo(3); 
} 
+0

Używam clang3.2 i g ++ 4.7.2, a twoja wersja wydaje się również "pracować". Ale po prostu próbowałem zastąpić twoją * foo (k) * z * foo (k + 1) * i zgadnij co, teraz mam różne wyjścia z dwóch kompilatorów :) (klang: 1, 2, 3 i gcc: 1 , 3, 3) –

+1

Nie chodzi o to, z jakiego kompilatora korzystasz; chodzi o to, że zachowanie jest niezdefiniowane. Oznacza to, że może działać w niektórych przypadkach, a awaria w innym. Co gorsza, może sformatować twardy dysk, zatrzymać dopływ tlenu lub spowodować inne uszkodzenia. –

+1

Myślę, że musisz dodać [kilka dodatkowych parametrów] (http://ideone.com/3B37yt), aby to zadziałało. –

Powiązane problemy