2017-12-01 1 views
7

Bedenken Sie:Ist es sicher, eine Mitgliedsreferenz zu erfassen, wenn die Klasse, die die ursprüngliche Referenz speichert, den Gültigkeitsbereich verlässt?

#include <iostream> 
#include <functional> 

std::function<void()> task; 
int x = 42; 

struct Foo 
{ 
    int& x; 

    void bar() 
    { 
     task = [=]() { std::cout << x << '\n'; }; 
    } 
}; 

int main() 
{ 
    { 
     Foo f{x}; 
     f.bar(); 
    } 

    task(); 
} 

Mein Instinkt war, dass, als der tatsächliche referent noch existiert, wenn die Aufgabe ausgeführt wird, wir eine neu gebundene Referenz zu dem Zeitpunkt erhalten das Lambda angetroffen wird und alles ist in Ordnung.

Allerdings, auf meinem GCC 4.8.5 (CentOS 7), sehe ich etwas Verhalten (in einem komplexeren Programm), die darauf hindeutet, dass dies stattdessen UB ist, weil f, und die Referenz selbst, gestorben sind. Ist das richtig?

+0

ich bin nicht sicher, ich verstehe. In Task binden Sie an 'int & x', das nicht mehr gültig ist, wenn' Foo' zerstört wird. – freakish

+5

Ich nehme an, ein "wörtliches" Lesen des Programms sagt, dass ich ein 'this' gefangen habe, das außerhalb des Geltungsbereichs liegt und dass' x' innerhalb der Aufgabe für ein jetzt ungültiges 'this -> x' kurz ist. –

+0

@freakish: Aber mache ich? Binde ich keine neue Variable an ':: x'? Woran ist 'r2' gebunden in' int x = 42; int & r1 = x; int & r2 = r1; '? –

Antwort

8

Mitglied Referenz So erfassen Sie die folgende Syntax verwenden, müssen (eingeführt in C++ 14):

struct Foo 
{ 
    int & m_x; 

    void bar() 
    { 
     task = [&l_x = this->m_x]() { std::cout << l_x << '\n'; }; 
    } 
}; 

diese Weise l_x ein int & in Schließung und auf den gleichen int Wert m_x war gespeichert ist Bezug und wird nicht von der Foo außerhalb des Geltungsbereichs gehen.

struct Foo 
{ 
    int & m_x; 

    void bar() 
    { 
     int * p_x = &m_x; 
     task = [=]() { std::cout << *p_x << '\n'; }; 
    } 
}; 
+2

Einverstanden. Nur verfügbar seit C++ 14 obwohl richtig? Ich denke, ich muss irgendwo einen Zeiger deklarieren und diesen Wert nehmen. –

+0

Obwohl meine Frage zu C++ 11 ist, ist dies nah genug, da es das Problem bestätigt und eine Lösung vorschlägt, die ich in Zukunft verwenden möchte - und die Konvertierung in eine C++ 11-Lösung ist jetzt trivial (und funktioniert übrigens). –

3

Sie erfassen ein Referenzelement in C++ 11 durch eine lokale Kopie der Referenz erstellen: Statt

In C++ 11 können wir diese Funktion fehlend von Wert-Erfassung einen Zeiger Umgehung und explizite Erfassung zu vermeiden this Erfassung:

void bar() 
{ 
    decltype(x) rx = x; // Preserve reference-ness of x. 
    static_assert(std::is_reference<decltype(rx)>::value, "rx must be a reference."); 
    task = [&rx]() { std::cout << rx << ' ' << &rx << '\n'; }; // Only capture rx by reference. 
} 
+0

Und das leidet nicht an dem gleichen Problem, nur weil wir den "this" Vermittler eliminiert haben? –

+0

@LightnessRacesinOrbit Ja, weil es explizite Capture 'this' verwendet wird nicht erfasst. Und wenn das Mitglied keine Referenz ist, bricht es zur Kompilierzeit ab. –

+0

Ich bekomme, dass 'this' nicht in Ihr Beispiel kommt - ich versuche, zu einem unzweifelhaften Beweis zu gelangen, dass diese eine Änderung in einem völlig gültigen Programm resultiert –

Verwandte Themen