2013-03-14 9 views
7

Kann jemand überprüfen, dass das folgende ein BUG ist, und warum erklären? Ich denke, ich weiß es, aber ich bin mir nicht im Klaren über die Details. (Meine eigentliche Problem involviert einen Vektor von Aufzählungen, nicht Ints, aber ich glaube nicht, dass es eine Rolle.) Angenommen, ich den folgenden Code haben:Referenz auf Element des Vektors, der von einer Funktion in C++ zurückgegeben wird

std::vector<int> f (void) { 
    std::vector<int> v; 
    v.push_back(5); 
    return v; 
} 

void g (void) { 
    const int &myIntRef = f()[0]; 
    std::cout << myIntRef << std::endl; 
} 

Bin ich richtig, dass myIntRef ist sofort ein baumelnden Referenz, weil Der Rückgabewert von f wird nirgendwo auf dem Stapel gespeichert?

Auch ist das folgende eine gültige Lösung, oder ist es immer noch ein Fehler?

Mit anderen Worten, wird das Rückgabeergebnis von f() weggeworfen, bevor das 0. Element kopiert werden kann?

+0

Ich bin nur neugierig, aber gibt es _ever_ irgendeinen Grund, ein 'int const &' anstelle eines 'int' als lokale Variable zu verwenden? –

+0

@JamesKanze: Vielleicht, um die Variable als "const" zu markieren? –

Antwort

2

Ja, es ist in der Tat falsch, etwas zu tun. Wenn Sie anrufen:

return v; 

temporäre Kopie des Objekts v wird erstellt und

const int &myIntRef = f()[0]; 

initialisiert Ihre Referenz mit dem ersten Elemente dieser temporären Kopie. Nach dieser Zeile existiert die temporäre Kopie nicht mehr, was bedeutet, dass myIntRef eine ungültige Referenz ist, deren undefiniertes Verhalten erzeugt.

Was sollten Sie tun, ist:

std::vector<int> myVector = f(); 
const int &myIntRef = myVector[0]; 
std::cout << myIntRef << std::endl; 

, die (dank Kopie elision) verwendet einen Zuweisungsoperator myVector Objekt zu initialisieren, indem v ohne Kopie v erstellt werden. In diesem Fall ist die Lebensdauer Ihrer Referenz gleich der Lebensdauer von myVector Objekt, so dass es perfekt gültigen Code.


Und zu Ihrer zweiten Frage: „Auch ist die folgende gültige fix, oder ist es ein Fehler, nach wie vor“

const int myIntCopy = f()[0]; // copy, not a reference 

Ja, das ist eine andere mögliche Lösung. f()[0] greift auf das erste Element der temporären Kopie zu und verwendet seinen Wert zur Initialisierung der Variablen myIntCopy.Es wird garantiert, dass die Kopie von v zurück von f() existiert zumindest bis der gesamte Ausdruck ausgeführt wird, siehe C++ 03 Standard-12.2 Temporäre Objekte §3:

Temporäre Objekte als letzten Schritt zerstört werden Bewerten des vollständigen Ausdrucks (1.9), der (lexikalisch) den Punkt enthält, an dem sie erstellt wurden.

+0

Vielen Dank. – user2170028

11

Das ist ein Fehler. Am Ende des vollständigen Ausdrucks const int &myIntRef = f()[0]; wird der temporäre Vektor zerstört und der Speicher freigegeben. Jede spätere Verwendung von myIntRef ist ein undefiniertes Verhalten.

Unter Umständen kann das Binden eines Verweises auf ein temporäres die Lebensdauer des temporären verlängern. Dies ist nicht einer dieser Fälle, der Compiler weiß nicht, ob die von std::vector<int>::operator[] zurückgegebene Referenz Teil des temporären oder eine Referenz auf eine int mit statischer Speicherdauer oder eine andere Sache ist, und es wird nicht die Lebensdauer verlängern.

+0

Großartig - vielen Dank. – user2170028

+0

Übrigens, ich habe eine Follow-up-Frage hinzugefügt, wenn Sie Lust haben :-). – user2170028

+0

@ user2170028: Antwort auf Ihre Follow-up-Frage ist: Ja. Siehe meine Antwort :) – LihO