2015-04-20 10 views
11

Die folgenden kompiliert. Aber gibt es jemals irgendeine Art von Referenzproblem?Kann ein C++ - Lambda-Konstruktor-Argument die konstruierte Variable erfassen?

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);}); 

Scheint zu arbeiten. (Das echte Lambda ist natürlich komplizierter.)

+0

Wahrscheinlich erwähnenswert, dass, wenn Sie das Lambda im Konstruktor verwenden, könnten schlimme Dinge passieren. –

+1

Wenn Sie es durch Kopieren erfassen, erhalten Sie eine Warnung mit clang 'Variable ist nicht initialisiert, wenn innerhalb ihrer eigenen Initialisierung verwendet ' – Drax

Antwort

5

Aber gibt es jemals irgendeine Art von hängendem Referenzproblem?

Das hängt ganz davon ab, was Sie mit Foo tun. Hier ist ein Beispiel, dass würde baumelnden Referenz Probleme haben:

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

So wäre es vernünftig zu sagen, mein Muster ist nicht gefährlicher als dangling Referenzen im Allgemeinen? Die Lebensdauer meines 'foo' ist in diesem Fall kein Problem. Ich fand die Konstruktion ziemlich komisch, indem ich einen Bezug auf ein Objekt einnehme, das immer noch konstruiert wird. –

+0

@AndrewLazarus Ja, im Grunde genommen. Einfach nur komisch. – Barry

4

Die Lambda-Ausdruck [&foo](int i){f(i, foo);} Compiler führt eine Verschluss Klasse, so etwas zu erzeugen (aber nicht ganz korrekt):

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

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

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

Daher Die Deklaration Foo foo([&foo](int i){f(i, foo);}); wird als Foo foo(_lambda(foo)); behandelt. Das Erfassen von foo selbst beim Konstruieren hat in dieser Situation kein Problem, da hier nur seine Adresse benötigt wird (Referenzen werden normalerweise über Zeiger implementiert).

Der Typ std::function<void(int)> intern kopieren diese Lambda-Typ konstruieren, was bedeutet, dass die Foo Konstruktorargument fn eine Kopie _lambda Objekt enthält (das hält eine Referenz (d.h. mFoo) auf Ihrer foo).

Dies bedeutet, dass baumelnden Referenz Problem in einigen Situationen auftreten kann, zum Beispiel:

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. 
} 
Verwandte Themen