2015-01-27 16 views
9

Ich weiß, dass der Standard eine Ausnahme hat, um die Lebensdauer von Provisorien zu verlängern, die im Grunde sagt, dass das Binden einer Konst-Referenz in einem Konstruktor die Lebensdauer nicht verlängert, aber gilt dies auch für Literale? Zum Beispiel:Kann ein const int ref in einem Konstruktor sicher an ein Literal binden?

class C { 
    private: 
     const int& ref; 
    public: 
     C(const int& in) 
      : ref{in} 
     { } 
}; 

Wenn ich eine Funktion der Rückkehr ein Objekt dieser Art

C f() { 
    C c(2); 
    return c; 
} 

Würde der Wert von c.ref in dem Anrufer nicht definiert werden musste, wenn ich wissen ist es zu einer wörtlichen gebunden?

+1

Die Antwort hängt wahrscheinlich, wenn der Compiler einen globalen statischen Wert für die wörtliche und zeigt auf das schafft, oder wenn es erzeugt einen Stapel Wert bei Punkt. Könnte in beide Richtungen gehen. –

+0

Warum nicht einfach den Wert int übergeben? Es wäre effizienter. – cppguy

+0

@cppguy Dies ist ein sehr minimales Beispiel für das eigentliche Problem. –

Antwort

6

Nein. Sie können die Referenz nicht verwenden, nachdem der Konstruktor die Ausführung beendet hat.
Wenn ein Prvalue des Nicht-Klassentyps an eine const -Referenz desselben Typs gebunden ist, wird immer ein Temporary eingeführt. Daher bezieht sich die Parameterreferenz des Konstruktors auf ein temporäres Objekt, das zerstört wird, sobald die Referenz den Gültigkeitsbereich verlässt. Nachdem dies geschehen ist, hängt die Memberreferenz, und der Versuch, auf den gespeicherten Wert hinter dieser Referenz zuzugreifen, führt zu UB. [dcl.init.ref]/5:

Ein Verweis auf den Typ „CV1T1“ durch einen Ausdruck von Typs initialisiert „CV2T2“ wie folgt:

  • Wenn die Referenz eine lvalue-Referenz ist und der Initialisierungsausdruck
    • ein lvalue (aber kein Bitfeld) ist und [..]
    • einen Klassentyp hat (d.h. T2 ist eine Typ-Klasse) [..]
  • Andernfalls wird die Referenz werden, um eine L-Wert Bezugnahme auf einen nichtflüchtigen const Typen (d.h., cv1 soll const) sein, oder die Referenz soll eine R-Referenz sein.

    • Wenn der Ausdruck Initialisierer

      • ist ein xValue (aber nicht ein Bit-Feld), Klasse prvalue, array prvalue oder Funktion lvalue und [..]
      • hat einen Klassentyp [ ..]
    • Ansonsten: (5.2.2.1)

      • Wenn T1 ist ein Klassentyp [..]
      • Wenn T1 ein nicht-Klasse-Typ ist, ein temporäre vom Typ „cv1T1“ erstellt wird und kopier initialisiert (8.5) aus dem initializer Ausdruck. Die Referenz wird dann an das temporäre gebunden.

Und Ganzzahlliterale sind, wenig überraschend, ja prvalues. [expr.prim.general]/1:

Ein String-Literal ist ein Lvalue; Alle anderen Literale sind Prvalues.

bei Schließlich ist diese unklar, [class.temporary]/5:

Die temporäre, an die das Referenz gebunden ist oder dass die temporäre das vollständige Objekt eines Subobjekt ist, zu dem

  • ein temporäre gebunden an ein Referenzelement in einem Konstruktor der ctor-Initialisierer (12.6.2) besteht, bis die const: wird das Referenz persistiert die gesamte Lebensdauer der Referenz außer gebundenen Rektor beendet.
+1

Auch im Standard: __Eine temporäre Bindung an ein Referenzelement in einem Konstruktor ctor-Initialisierer (§12.6.2) bleibt bestehen, bis der Konstruktor exits__ – MatiasFG

+0

Nice! Ich habe mir das erst kürzlich angeschaut, deshalb hatte ich die Referenz (Wortspiel beabsichtigt) zur Hand. – MatiasFG

+0

@MatiasFG Ja, aber ich wusste nicht wirklich, wo ich suchen sollte (ich wollte nur suchen). Vielen Dank! – Columbo

1

Kurze Antwort: Die Auswertung c.ref wird fast sicher illegal sein (undefiniertes Verhalten aufrufen).

Lange Antwort: Wenn eine Referenz auf ein Ganzzahlliteral Bindung, was Sie wirklich tun, ist die folgende:

Die Ganzzahlliteral bezieht sich auf das, was als „a value that is not associated with an object“ bekannt ist.

Um einen Verweis darauf zu binden, muss ein Objekt erstellt werden, das denselben Wert enthält. Der Grund dafür ist, dass eine Referenz (oder ein Zeiger) immer auf ein Objekt zeigen muss (was wiederum nicht mehr als ein bisschen Speicher ist). Daher wird ein temporäres Objekt erstellt, das den Wert enthält.

Temporäre Objekte sind garantiert so lange gültig, wie der Ausdruck, durch den sie erstellt werden, ausgewertet wird. Da Ihr Objekt länger existiert, wird das temporäre Objekt, das Ihren Wert enthielt, früh zerstört und die Referenz kann nicht mehr aufgerufen werden.

Beachten Sie, dass wenn Sie auf c.ref innerhalb des Ausdrucks zugreifen, der c erstellt wurde, würden Sie tatsächlich in Ordnung sein.