2013-06-21 23 views
5

Während this question fragen, ich const Verweis auf ein temporäres Objekt gelernt gilt in C++:const Verweis auf ein temporäres Objekt wird nach Funktionsumfang (Lebenszeit) gebrochen

int main() 
{ 
    int a = 21; 
    int b = 21; 

    //error: invalid initialization of non-const reference 
    //int  & sum = a + b;e [...] 

    //OK 
    int const & sum = a + b; 

    return sum; 
} 

Aber in dem folgenden Beispiel die const Die Referenz refnop bezieht sich auf ein zerstörtes temporäres Objekt. Ich wundere mich warum?

#include <string> 
#include <map> 

struct A 
{ 
    // data 
    std::map <std::string, std::string> m; 
    // functions 
    const A& nothing()   const { return *this; } 
    void init()      { m["aa"] = "bb"; } 
    bool operator!= (A const& a) const { return a.m != m; } 
}; 

int main() 
{ 
    A a; 
    a.init(); 

    A const& ref = A(a); 
    A const& refnop = A(a).nothing(); 

    int ret = 0; 
    if (a != ref)  ret += 2; 
    if (a != refnop) ret += 4; 

    return ret; 
} 

mit Getestet GCC 4.1.2 und MSVC 2010 es 4 zurückkehrt;

$> g++ -g refnop.cpp 
$> ./a.out ; echo $? 
4 

Der Unterschied zwischen ref und refnop ist der Aufruf nothing(), die wirklich nichts tut. Nach diesem Aufruf scheint das temporäre Objekt zerstört zu sein!

Meine Frage:
Warum im Fall von refnop, ist die Lebenszeit des temporären Objekts als konstante Referenz nicht das Gleiche?

+0

Vorsicht: mit g ++ Versionen 4.4 und 4.6, Dieses Snippet gibt 0 zurück ... – olibre

Antwort

9

Die Lebensdauerverlängerung eines temporären Objekts kann nur einmal ausgeführt werden, wenn das temporäre Objekt an die erste Referenz gebunden wird. Danach ist das Wissen, dass die Referenz auf ein temporäres Objekt verweist, weg, so dass weitere Lebensdauerverlängerungen nicht möglich sind.

Der Fall, dass Sie

A const& refnop = A(a).nothing(); 

ist rätselhaft ist in diesem Fall ähnlich:

A const& foo(A const& bar) 
{ 
    return bar; 
} 
//... 
A const& broken = foo(A()); 

In beiden Fällen wird die temporäre auf das Funktionsargument gebunden (die implizite this für nothing(), bar für foo()) und erhält seine Lebensdauer 'erweitert' auf die Lebensdauer des Funktionsarguments. Ich setze 'Extended' in Anführungszeichen, weil die natürliche Lebensdauer des Temporären schon länger ist, also findet keine tatsächliche Erweiterung statt.

Da die Verlängerung der Lebensdauer-Eigenschaft ist nicht transitiv, einen Verweis zurückkehr (das in einem temporären Objekt verweisen geschieht) wird die Lebensdauer des temporären Objekt nicht weiter verlängern, wobei als Ergebnis, dass sowohl refnop und broken am Ende zu beziehen Objekte, die nicht mehr existieren.

1

Mein ursprüngliches Beispiel ist komplex.

Deshalb poste ich hier ein einfacheres Beispiel und stelle den entsprechenden ISO C++ standard Absatz zur Verfügung.

Dieses einfacheres Beispiel ist auch auf coliru.stacked-crooked.com/

#include <iostream> 

struct A 
{ 
    A(int i) { std::cout<<"Cstr "<< i<<'\n'; p = new int(i); } 
~A()  { std::cout<<"Dstr "<<*p<<'\n'; delete p;  } 

    const A& thiz() const { return *this; } 

    int *p; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

int main() 
{ 
    const A& a4 = A(4); 
    const A& a5 = A(5).thiz(); 
    const A& a6 = constref(A(6)); 

    std::cout << "a4 = "<< *a4.p <<'\n'; 
    std::cout << "a5 = "<< *a5.p <<'\n'; 
    std::cout << "a6 = "<< *a6.p <<'\n'; 
} 

Die Ausgangsbefehlszeile g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out mit:

Cstr 4 
Cstr 5 
Dstr 5 
Cstr 6 
Dstr 6 
a4 = 4 
a5 = 0 
a6 = 0 
Dstr 4 

Wie Sie sehen können, die temporären Objekte referenziert von a5 und a6 sind zerstört bei der Ende der Funktionen thiz bzw. constref.

Dies ist ein Extrakt aus Kapitel 12.2 temporäre Objekte, wobei der fettgedruckte Teil in diesem Fall gilt:

Der zweite Rahmen ist, wenn eine Referenz auf ein temporäres gebunden ist. Die temporäre, an der das Referenz gebunden ist oder die temporäre , die das vollständige Objekt eines Subobjekt ist, an dem die Referenz bleibt für die gesamte Lebensdauer der Referenz außer gebunden ist:

  • ist ein vorübergehende gebunden an ein Bezugselement in einem Konstruktor Ctor-Initialisierer (12.6.2) bleibt bestehen, bis der Konstruktor beendet wird.
  • Eine temporäre Bindung an einen Referenzparameter in einem Funktionsaufruf (5.2.2) bleibt bestehen bis zum Abschluss des vollständigen Ausdrucks, der den Aufruf enthält.
  • Die Lebensdauer einer temporären Bindung an den zurückgegebenen Wert in eine Funktion Return-Anweisung (6.6.3) wird nicht erweitert; Das temporäre wird am Ende des vollständigen Ausdrucks in der return-Anweisung zerstört.
  • Ein temporäre gebunden an eine Referenz in einem new-Initialisierer (5.3.4) bestehen bleibt bis zum Abschluss des Voll Ausdrucks enthält den new-Initialisierer.

Dies ist ein vollständigeres Beispiel:

#include <iostream> 

struct A 
{ 
    A()   { std::cout<<"Cstr 9\n";   p = new int(v = 9);  } 
    A(int i) { std::cout<<"Cstr "<<i<<'\n'; p = new int(v = i);  } 
    A(const A&o){ std::cout<<"Copy "<<o.v<<'\n'; p = new int(v = 10+o.v); } 
    ~A()   { std::cout<<"Del "<<v<<' '<<*p<<'\n'; *p = 88; delete p; } 

    const A& thiz() const { return *this; } 

    int *p; 
    int v; 
}; 

const A& constref(const A& a) 
{ 
    return a; 
} 

std::ostream& operator<<(std::ostream& os, const A& a) 
{ 
    os <<"{ *p="<< *a.p <<" , v="<< a.v <<" }\n"; 
    return os; 
} 

int main() 
{ 
    std::cout << "---const A a1 = A(1)"    "\n"; 
        const A a1 = A(1); 
    std::cout << "---const A a2 = A(2).thiz()"   "\n"; 
        const A a2 = A(2).thiz(); 
    std::cout << "---const A a3 = constref(A(3))" "\n"; 
        const A a3 = constref(A(3)); 
    std::cout << "---const A& a4 = A(4)"    "\n"; 
        const A& a4 = A(4); 
    std::cout << "---const A& a5 = A(5).thiz()"   "\n"; 
        const A& a5 = A(5).thiz(); 
    std::cout << "---const A& a6 = constref(A(6))" "\n"; 
        const A& a6 = constref(A(6)); 

    std::cout << "a1 = "<< a1; 
    std::cout << "a2 = "<< a2; 
    std::cout << "a3 = "<< a3; 
    std::cout << "a4 = "<< a4; 
    std::cout << "a5 = "<< a5; 
    std::cout << "a6 = "<< a6; 
} 

und die entsprechende Ausgabe mit gleicher g++ Befehlszeile:

---const A a1 = A(1) 
Cstr 1 
---const A a2 = A(2).thiz() 
Cstr 2 
Copy 2 
Del 2 2 
---const A a3 = constref(A(3)) 
Cstr 3 
Copy 3 
Del 3 3 
---const A& a4 = A(4) 
Cstr 4 
---const A& a5 = A(5).thiz() 
Cstr 5 
Del 5 5 
---const A& a6 = constref(A(6)) 
Cstr 6 
Del 6 6 
a1 = { *p=1 , v=1 } 
a2 = { *p=12 , v=12 } 
a3 = { *p=13 , v=13 } 
a4 = { *p=4 , v=4 } 
a5 = { *p=0 , v=5 } 
a6 = { *p=0 , v=6 } 
Del 4 4 
Del 13 13 
Del 12 12 
Del 1 1