2013-03-25 5 views
7

Angenommen, ich habe folgendes.Lambda Ausdruck hinterlistig Capture `this`

struct A 
{ 
    int x; 
    std::function<int()> f1() { return [=](){ return x; }; } 
    std::function<int()> f2() { return [=](){ return this->x; }; } 
    std::function<int()> f3() { return [this](){ return x; }; } 
    std::function<int()> f4() { return [this](){ return this->x; }; } 
    std::function<int()> f5() 
    { 
     int temp = x; 
     return [=](){ return temp; }; 
    } 
} 

Und jetzt habe ich den folgenden Code.

auto a = std::make_shared<A>(); 
a->x = 5; 
std::function<int()> f = a.f#(); 
a.reset(); 
int x = f(); 

wo die f# einem der f1, f2, f3, f4, f5 bezieht.

Diese Funktionen Verhalten in einem der zwei Sätze stellen aus:

  1. Rücklauf 5, wenn sie aufgerufen (f5) oder
  2. abgestürzt zu dereferenzieren versucht nullptr (f1, f2, f3, f4).

Ich verstehe, dass dies, weil einige sind „this“ in der Elementfunktion A, entweder implizit oder explizit zu erfassen.

Welche formale Regel bestimmt Verhalten 1 oder 2?

Ich verbrachte eine Weile mit einem Fehler zu tun, die durch etwas Ähnliches wie f1 verursacht wurde, es würde erfassen x denken und nie this würde erfassen bedenkt, so dachte ich, es wäre nützlich, dies dokumentiert zu werden.

Antwort

8

Es gibt keine formale Regel, die dieses Verhalten feststellt. Dieses Verhalten ist undefined.

Ihre Lambdas greifen auf ein Objekt zu, das nicht existiert. Sie können eine Elementvariable nicht direkt nach Wert erfassen. Sie erfassen sie immer durch this. Was bedeutet, dass Sie sie als Referenz erfassen. Sobald das Objekt gelöscht wurde, führt jeder Versuch, auf dieses gelöschte Objekt zuzugreifen, zu undefiniertem Verhalten.

Die Ausnahme ist f5, die einen konsistenten Wert, garantiert, zurückgeben sollte. Es ist vollständig vom Ursprungsobjekt getrennt.

+0

Oh - Ich beschriftet die Gruppen 'f1, f2, f3, f4' und' f5' rückwärts! Ja, ich meinte, es war 'f5', das richtig funktioniert und die anderen nicht. Bearbeitet. –