2012-12-09 30 views
7

z mojego doświadczenia wydaje się, że albo:C++ Adres obiektów lambda jako parametry do funkcji

  • Wyrażenie lambda tworzone wewnątrz wywołania funkcji jest zniszczona tuż po wywołaniu
  • wywołanie funkcji, która spodziewa się std::function tworzy tymczasowy obiekt (std :: function) z lambda i że obiekt jest niszczony po wywołaniu

Takie zachowanie można zaobserwować na poniższym fragmencie kodu:

const function<void()>* pointer; 

void a(const function<void()> & f) 
{ 
    pointer = &f; 
} 

void b() 
{ 
    (*pointer)(); 
} 

int main() 
{ 
    int value = 1; 
    std::cout << &value << std::endl; 

    // 1: this works  
    function<void()> f = [&]() { std::cout << &value << std::endl; }; 
    a(f); 

    // 2: this doesn't 
    a([&]() { std::cout << &value << std::endl; }); 

    /* modify the stack*/ 
    char data[1024]; 
    for (int i = 0; i < 1024; i++) 
     data[i] = i % 4; 

    b(); 

    return 0; 
} 

Co dokładnie dzieje się w drugim przypadku? Czy istnieje poprawny sposób wywoływania a() bez tworzenia jawnego obiektu std::function?

Edit:: Ten obie wersje (1 i 2) skompilować tylko prawo ale skutkować różnymi wyjściami:

Wersja 1:

0x7fffa70148c8 
0x7fffa70148c8 

Wersja 2:

0x7fffa70148c8 
0 
+1

Co masz na myśli, że drugi przypadek "nie działa"? Czy to się kompiluje? Czy to się psuje? Czy wypisuje "nie działa" na twojej drukarce? – jalf

+1

@jalf: Zapomniałeś: czy to się zatrzymuje i się pali. – Grizzly

+1

/* modyfikowanie stosu */Większość kompilatorów wstępnie przydziela miejsce dla ** wszystkich ** zmiennych lokalnych po wprowadzeniu funkcji. –

Odpowiedz

2

Jeśli utworzysz tymczasowy, zniknie na końcu wiersza. Oznacza to, że przechowywanie wskaźnika do niego jest złym pomysłem, jak słusznie stwierdzono.

Jeśli chcesz zapisać wskaźnik na std::function (lub cokolwiek innego), upewnij się, że jego żywotność nie kończy się, zanim przestaniesz używać wskaźnika. Oznacza to, że naprawdę potrzebujesz nazwanego obiektu typu std::function.

Co się dzieje w drugim przypadku: Tworzysz tymczasową lambdę, która ma zostać przekazana do funkcji. Ponieważ funkcja oczekuje wartości std::function, z lambda zostanie utworzone tymczasowe std::function. Oba zostaną zniszczone na końcu linii. W związku z tym masz teraz wskaźnik do już zniszczonego tymczasowego, co oznacza, że ​​próba użycia wskazanego obiektu przyniesie Cię mocno na terytorium nieokreślonego zachowania.

+0

dobrze, nie jest to konieczne dla lambdas, ponieważ są one przepisywane na "prawdziwe" funkcje, więc pozostają w pamięci, pod tym samym adresem . –

+0

Na końcu zawierającego _full expression_ faktycznie. W tym przypadku jest to w przybliżeniu ta linia. – sehe

+0

@BarnabasSzabolcs: Jesteś tego pewien? Wartość lambda może zostać ostatecznie przekształcona w wskaźnik funkcji (więc funkcja jest gdzieś, tyle jest prawdą), ale czy lambda zostanie przepisana tak, aby była identyczna z tą funkcją? wydaje się marnotrawstwem pod względem nieodebranej szansy. Tak czy siak, to nie ma znaczenia, ponieważ ważne jest to, że 'std :: function' jest zniszczona – Grizzly

1

To jest w porządku dla bezpaństwowców lambdas. Bezstanowe lambdy mają niejawną konwersję na typ wskaźnika funkcji.

Ponadto istnieje zawsze niejawną konwersję na std :: function <> niezależnie od rzeczywistego typu wywoływalnego.

Występują problemy z utrzymaniem wskaźnika na tymczasowe, ale. Nie zauważyłem tego podczas pierwszego czytania kodu.

To oczywiście nie ma nic wspólnego ze std :: function.

+0

Dobrze jest zapisać wskaźnik do tymczasowej 'std :: function', jeśli obiekt zawinięty wewnątrz' function' jest wskaźnikiem funkcji? – Grizzly

+0

Nie. Wskaźniki do tymczasowych są zawsze złe. Wiesz, to nie ma nic wspólnego ze std :: function <> naprawdę – sehe

+0

** Wiem o tym, ale twoja odpowiedź zdaje się sugerować, że tak jest (więc możesz chcieć zmienić treść;)) – Grizzly

0

Tymczasowe obiekty są niszczone na końcu instrukcji, w której zostały utworzone. Właśnie pokazałeś to z lambda.

Funkcja standardowa, którą przekazujesz za pierwszym razem, nie jest tymczasowa, więc trwa tak długo jak zmienna. W rzeczywistości przechowuje kopię tymczasowego.

Aby Twoje lamdały trwały dłużej niż jedna linia, musisz je gdzieś przechowywać i określić czas ich życia.

Istnieje wiele sposobów, aby to zrobić. Funkcja Std jest sposobem usuwania typu opartym na typie - to znaczy, zapisuje funkcję std zamiast wskaźnika do funkcji standardowej. Zmień typ pointer na niezawierający wskaźnika (i nie stały), pozbądź się & po przypisaniu go i wywołaj go bezpośrednio.

Ogólnie unikaj przyjmowania i przechowywania adresu parametrów funkcji referencji stałych jako tymczasowego powiązania z nimi, chyba że złapiesz możliwość odniesienia rwartości w innym przeciążeniu.

+0

Dowolny sposób na zapisanie wartości lambda bez konieczności nakładania środowiska wykonawczego na typ wymazania dostarczony przez funkcję std :: function? –

+0

@joaorafael W funkcji, auto dedukuje typ. W klasie szablonu zawijającej wywołanie twórca oparty na funkcjach może wydedukować typ lambda i przekazać go do klasy, aby klasa mogła przechowywać nieusuniętą wartość lambda. Filozoficzne podejście może cię zapytać, jeśli wiesz, że jest to wąskie gardło wydajności i jeśli twoje wysiłki nie są lepiej wydawane gdzie indziej. Kod, który przechowuje i używa lambda, musi być funkcją typu lambdas, jeśli chcesz uniknąć wymazywania typu. – Yakk

Powiązane problemy