2015-04-20 17 views
11

Il seguente compila. Ma c'è mai qualche tipo di problema di riferimento ciondolante?Un argomento del costruttore lambda in C++ può acquisire la variabile costruita?

class Foo { 
     Foo(std::function<void(int)> fn) { /* etc */ } 
    } 

    void f(int i, Foo& foo) { /* stuff with i and foo */ } 

    Foo foo([&foo](int i){f(i, foo);}); 

Sembra funzionare. (Il vero lambda è ovviamente più complicato.)

+0

Probabilmente la pena notare che se si utilizza il lambda nel costruttore, cose cattive potrebbe accadere. –

+1

se lo si cattura per copia si ottiene un avviso con clang 'la variabile non è inizializzata se utilizzata all'interno della propria inizializzazione' – Drax

risposta

5

Ma c'è mai qualche tipo di problema di riferimento ciondolante?

Questo dipende interamente da ciò che si sta facendo con . Ecco un esempio che avrebbe hanno problemi di riferimento penzoloni:

struct Foo { 
    Foo() = default; 
    Foo(std::function<void(int)> fn) : fn(fn) { } 
    std::function<void(int)> fn; 
} 

Foo outer; 
{ 
    Foo inner([&inner](int i){f(i, inner);}); 
    outer = inner; 
} 
outer.fn(42); // still has reference to inner, which has now been destroyed 
+1

Quindi sarebbe ragionevole dire che il mio modello non è più pericoloso dei riferimenti ciondolanti in generale? In questo caso, la durata del mio 'foo' non è un problema. Ho trovato la costruzione piuttosto strana, catturando un riferimento a un oggetto che è ancora in fase di costruzione. –

+0

@AndrewLazarus Sì, fondamentalmente. Proprio strano – Barry

4

Il lambda espressione [&foo](int i){f(i, foo);} porterà compilatore di generare una classe di chiusura qualcosa di simile (ma non del tutto corretto):

class _lambda 
{ 
    Foo& mFoo; // foo is captured by reference 

public: 
    _lambda(Foo& foo) : mFoo(foo) {} 

    void operator()(int i) const 
    { 
     f(i, mFoo); 
    } 
}; 

Pertanto, la dichiarazione Foo foo([&foo](int i){f(i, foo);}); viene considerata come Foo foo(_lambda(foo));. Catturare lo stesso foo durante la costruzione non presenta problemi in questa situazione, poiché qui è richiesto solo il suo indirizzo (i riferimenti vengono generalmente implementati tramite puntatori).

Il tipo std::function<void(int)> sarà internamente copiare costruire questo tipo lambda, il che significa che l'argomento il costruttore di Foo fn contiene una copia di _lambda dell'oggetto (che contiene un riferimento (cioè, mFoo) al foo).

Questi implica che problema di riferimento penzoloni può verificarsi in alcune situazioni, ad esempio:

std::vector<std::function<void(int)>> vfn; // assume vfn live longer than foo 

class Foo { 
    Foo(std::function<void(int)> fn) { vfn.push_back(fn); } 
} 

void f(int i, Foo& foo) { /* stuff with i and foo */ } 

Foo foo([&foo](int i){f(i, foo);}); 

.... 

void ff() 
{ 
    // assume foo is destroyed already, 
    vfn.pop_back()(0); // then this passes a dangling reference to f. 
} 
Problemi correlati