2015-01-26 1 views
13

Es gibt Fälle, wenn ich einen Verweis auf ein Objekt möchte, aber stattdessen bekomme ich eine Kopie. Hier ist ein Beispiel:Wann und warum bekomme ich beim Initialisieren einer Referenz eine Kopie?

std::pair<const std::string, int> foo("hello", 5); 
    const std::pair<std::string, int> & bar = foo; 

    std::cout << "foo: " << foo.first << " " << foo.second << std::endl; 
    std::cout << "bar: " << bar.first << " " << bar.second << std::endl; 
    foo.second = 7; 
    std::cout << "foo: " << foo.first << " " << foo.second << std::endl; 
    std::cout << "bar: " << bar.first << " " << bar.second << std::endl; 

Dies erzeugt:

foo: hello 5 
bar: hello 5 
foo: hello 7 
bar: hello 5 

So offenbar eine Kopie foo erstellt wurde, während die Syntax (für mich zumindest) schlägt vor, dass der Programmierer einen Verweis darauf wollte . Dies verstößt gegen das Prinzip, dass eine Referenz ein Alias ​​für etwas sein sollte. Es wäre großartig, wenn jemand erklären könnte, was vor sich geht und warum.

(Anmerkung: I kam in diesem here)

+3

Die Typen sind unterschiedlich. – Borgleader

Antwort

15

Die grundlegenden Typen von foo und bar unterschiedlich sind, so dass eine vorübergehende geschaffen ist eine implizite Umwandlung von dem Typ auf der RHS Verwendung mit der auf der LHS *. Der C++ - Standard ermöglicht es einem const Verweis, an ein temporäres Objekt zu binden und dessen Lebensdauer zu verlängern.

Die const Referenz bar bindet an das temporäre, das ein eindeutiges Objekt aus foo ist.

Wenn Sie die gleichen Typen verwenden waren, das Ergebnis erhalten, würden Sie erwarten:

std::pair<const std::string, int> foo("hello", 5); 
const std::pair<const std::string, int> & bar = foo; 

oder

std::pair<std::string, int> foo("hello", 5); 
const std::pair<std::string, int> & bar = foo; 

foo: hello 5 
bar: hello 5 
foo: hello 7 
bar: hello 7 
ergeben würde

* std::pair hat eine template constructor, die diese implizite Konvertierung von einem Paartyp zu einem anderen ermöglicht.

+1

Und in welchen Fällen wird das Zuweisen inkompatibler Typen ein temporäres erstellen, anstatt nur einen Compilerfehler zu werfen? – Sarien

+0

@Sarien Es muss eine gültige Konvertierung zwischen "T1" und "T2" in "const T2 & = T1"; – juanchopanza

+0

@Sarien der Compiler sucht nach einer brauchbaren Konvertierung. Es kann den Konstruktor von 'Paar' mit Kopien des Wertes im ursprünglichen Paar aufrufen, also wird es tun. –

5

Das ist eine spezielle Eigenschaft von Verweisen auf const (und natürlich von rvalue references). Diese Referenzen können an temporäre Objekte binden.

Hinweis, dass std::pair<const std::string, int> (die Art der foo) ist eine andere Art als std::pair<std::string, int> (die Art, auf die Bezug zu nehmen will bar, modulo const). In Ihrem Code befindet sich kein Objekt vom Typ std::pair<std::string, int>, daher kann bar nicht an ein solches Objekt gebunden werden.

Wie bereits erwähnt, können Referenzen auf const und rvalue-Referenzen an Provisorien gebunden werden. Und ein Objekt vom Typ std::pair<std::string, int> kann implizit aus einem Objekt vom Typ std::pair<const std::string, int> erstellt werden. Daher wird ein solches temporäres Objekt erstellt und bar ist an dieses temporäre gebunden. Diese Referenzbindung verlängert auch die Lebensdauer des Provisoriums auf die von bar*.

Deshalb erhalten Sie eine Kopie. Wenn Sie den Typ bar in std::pair<std::string, int> & geändert haben (d. H. Die const gelöscht haben), wird stattdessen ein Kompilierungsfehler angezeigt, der besagt, dass eine nicht konstante lvalue-Referenz nicht an eine temporäre Bindung gebunden werden kann.


* Im Sonderfall eines Mitglied Variable vom Typ Referenz auf einen temporären gebunden, wird die temporäre Lebenszeit nur bis zum Ende der Konstruktors erstrecken, die die Referenz initialisiert.