Fehler: Ungültige Initialisierung der nicht konstanten Referenz vom Typ 'int &' aus einem Wert vom Typ 'int'

86

Falsche Form:

int &z = 12;

Richtige Form:

int y;
int &r = y;

Frage :
Warum ist der erste Code falsch? Was ist die " Bedeutung " des Fehlers im Titel?

Wassermann_Girl
quelle
4
Temporäre können nicht an nicht konstante Referenzen gebunden werden. int (12) ist in diesem Fall vorübergehend.
Prasoon Saurav
@PrasoonSaurav Was meinst du mit temporären 12? Fehlende Konzepte hier (meinerseits :))
Aquarius_Girl
2
Beachten Sie, dass es keinen strengen technischen Grund für diese Einschränkung gibt. Es wäre genauso einfach zu implementieren gewesen, um veränderbare Verweise auf Provisorien zuzulassen . Das Verbot ist eine Entwurfsentscheidung von C ++, da eine solche Konstruktion ein schlechtes Design mit einem weitaus höheren Risiko eines versehentlichen Missbrauchs wäre als ein echtes Dienstprogramm. (Ich habe nur einmal einen erfundenen Bedarf für so etwas gefunden.)
Kerrek SB
@ KerrekSB die häufigste Notwendigkeit für eine Bindung ref zu rvalue Objekt ist wahrscheinlich(ostringstream() << "x=" << x).str()
neugierigguy
@curiousguy: Ja, das ist der Inhalt des Links, den ich gepostet habe.
Kerrek SB

Antworten:

130

In C ++ 03 3.10 / 1 heißt es: "Jeder Ausdruck ist entweder ein l-Wert oder ein r-Wert." Es ist wichtig, sich daran zu erinnern, dass Wertigkeit gegenüber Wertigkeit eine Eigenschaft von Ausdrücken ist, nicht von Objekten.

L-Werte benennen Objekte, die über einen einzelnen Ausdruck hinaus bestehen bleiben. Zum Beispiel obj, *ptr, ptr[index]und ++xsind alle lvalues.

R-Werte sind Provisorien, die am Ende des vollständigen Ausdrucks, in dem sie leben, verdunsten ("am Semikolon"). Beispielsweise,1729 , x + y, std::string("meow")und x++sind alle rvalues.

Die Adresse des Operators verlangt, dass sein "Operand ein Wert ist". Wenn wir die Adresse eines Ausdrucks nehmen könnten, wäre der Ausdruck ein l-Wert, andernfalls ein r-Wert.

 &obj; //  valid
 &12;  //invalid
BruceAdi
quelle
2
" Wenn wir die Adresse eines Ausdrucks nehmen könnten, wäre der Ausdruck ein Wert, andernfalls ein Wert. " Wenn nur C ++ so einfach wäre! (aber die Nuance ist hier nicht wirklich relevant) "Rvalues ​​are temporaries" temporär was? Objekte?
Neugieriger
1
@curiousguyRvalues ​​sind temporäre Werte, die am Ende des vollständigen Ausdrucks, in dem sie leben, verschwinden. Um einfach zu beantworten, warum der Express int & = 12;ungültig ist, sagt der Standard, dass ein String-Literal ein l-Wert ist, andere Literale sind r-Werte.
BruceAdi
@curiousguy: Ja. R-Werte sind temporäre Objekte , aber nicht alle r-Werte sind temporäre Objekte. Einige sind nicht einmal Objekte.
Nawaz
" Zum Beispiel sind 1729, x + y, std :: string (" meow ") und x ++ alle r-Werte. " std::string("meow")Konstruiert jedoch ein Objekt vom Typ std::stringund liefert einen r-Wert, der dieses Objekt bezeichnet, 1729keine Nebenwirkungen hat und einen Wert liefert 1729 als Wert des Typs int.
Neugieriger
" L-Werte benennen Objekte, die über einen einzelnen Ausdruck hinaus bestehen. " Falsch, nehmen Sie:(const int&)1;
neugieriger Kerl
52
int &z = 12;

Auf der rechten Seite intwird aus dem integralen Literal ein temporäres Objekt vom Typ erstellt , das temporäre Objekt 12kann jedoch nicht an eine nicht konstante Referenz gebunden werden. Daher der Fehler. Es ist dasselbe wie:

int &z = int(12); //still same error

Warum wird eine temporäre erstellt? Da sich eine Referenz auf ein Objekt im Speicher beziehen muss und ein Objekt vorhanden sein muss, muss es zuerst erstellt werden. Da das Objekt unbenannt ist, handelt es sich um ein temporäres Objekt. Es hat keinen Namen. Aus dieser Erklärung wurde ziemlich klar, warum der zweite Fall in Ordnung ist.

Ein temporäres Objekt kann an eine const-Referenz gebunden werden. Dies bedeutet, dass Sie Folgendes tun können:

const int &z = 12; //ok

C ++ 11 und Rvalue Referenz:

Der Vollständigkeit halber möchte ich hinzufügen, dass C ++ 11 eine rvalue-Referenz eingeführt hat, die an ein temporäres Objekt gebunden werden kann. In C ++ 11 können Sie also Folgendes schreiben:

int && z = 12; //C+11 only 

Beachten Sie, dass es &&intead von gibt &. Beachten Sie auch, dass dies constnicht mehr benötigt wird, obwohl das Objekt, an das zgebunden wird, ein temporäres Objekt ist, das aus einem Integral-Literal erstellt wurde 12.

Da C ++ 11 rvalue-reference eingeführt hat , int&wird es nun als lvalue-reference bezeichnet .

Nawaz
quelle
10

12ist eine Konstante zur Kompilierungszeit, die im Gegensatz zu den Daten, auf die verwiesen wird, nicht geändert werden kann int&. Was Sie tun können , ist

const int& z = 12;
Michael Krelin - Hacker
quelle
2
@curiousguy, sei konsequent, du hast gesagt "Du musst verstehen, dass dies C ++ - Regeln sind. Sie existieren und brauchen keine Begründung" (ganz zu schweigen von der ersten Ausgabe). Wie soll ich Ihre Beschwerde interpretieren, nicht zu geben, was Ihrer Meinung nach nicht benötigt wird?
Michael Krelin - Hacker
2
@ MichaelKrelin-hacker: Technisch gesehen kann man (nie) keinen Verweis auf einen Wert (oder eine Kompilierungszeitkonstante) binden, der Standard ist ziemlich explizit, was tatsächlich passiert: Andernfalls wird ein Temporär vom Typ "cv1 T1" erstellt und Initialisiert aus dem Initialisierungsausdruck unter Verwendung der Regeln für eine Nichtreferenzkopie-Initialisierung (8.5). Die Referenz wird dann an das Temporäre gebunden. Das heißt, die Syntax ist zulässig, aber die Semantik besteht nicht darin, eine Referenz an die Konstante zu binden, sondern sie an das Temporäre zu binden, das implizit erstellt wird.
David Rodríguez - Dribeas
1
@curiousguy: Die Regeln der Sprache sind Teil des Designs, und meistens gibt es Rechtfertigungen dafür, warum die Sprache so gestaltet wurde. In diesem speziellen Fall, wie in C, dürfen Sie weder die Adresse eines Wertes übernehmen (es hat keinen), noch können Sie eine Referenz binden. Betrachten Sie nun eine Funktion void f( vector<int> const & ), die idiomatisch ist, um einen Vektor zu übergeben, der nicht geändert werden soll. Das Problem ist jetzt, dass f( vector<int>(5) )dies falsch wäre und der Benutzer eine andere Überlastung bereitstellen müsste, void f( vector<int> v ) { f(v); }was trivial ist.
David Rodríguez - Dribeas
1
... nun, da dies für die Benutzer der Sprache schmerzhaft wäre, haben die Designer entschieden, dass der Compiler die entsprechende Operation für Sie ausführen würde. Beim Aufruf erstellt f( vector<int>(5) )der Compiler eine temporäre und bindet dann den Verweis auf diese temporäre und ähnliche wenn es eine implizite Konvertierung von 5direkt gab. Dies ermöglicht dem Compiler, eine einzelne Signatur für die Funktion zu generieren, und ermöglicht eine Einzelbenutzerimplementierung der Funktion. Von da an wird ein ähnliches Verhalten für den Rest der Verwendung konstanter Referenzen zur Konsistenz definiert.
David Rodríguez - Dribeas
1
@ MichaelKrelin-Hacker: Verweise sind Aliase auf Objekte, und ein Wert ist kein Objekt. Abhängig vom Kontext können Referenzen nur ein Alias ​​sein. Der Compiler entfernt die Referenz und verwendet den Bezeichner, um zu bedeuten, was auch immer das ursprüngliche Objekt bedeutet ( T const & r = *ptr;jede spätere Verwendung rin der Funktion kann durch ersetzt werden *ptrund rmuss zur Laufzeit nicht vorhanden sein). oder es muss möglicherweise implementiert werden, indem die Adresse des Objekts beibehalten wird, das es als Aliase verwendet (speichern Sie eine Referenz als Mitglied eines Objekts) - das als automatisch referenzierter Zeiger implementiert ist.
David Rodríguez - Dribeas
4

Non-Const- und Const-Referenzbindung folgen unterschiedlichen Regeln

Dies sind die Regeln der C ++ - Sprache:

  • ein Ausdruck bestehend aus einer Literalzahl (12 Literalzahl ) besteht, ist ein "rWert".
  • Es ist nicht zulässig, eine nicht konstante Referenz mit einem r-Wert zu erstellen: int &ri = 12; ist schlecht geformt
  • Es ist zulässig, eine const-Referenz mit einem r-Wert zu erstellen. In diesem Fall wird vom Compiler ein unbenanntes Objekt erstellt. Dieses Objekt bleibt bestehen, solange die Referenz selbst vorhanden ist.

Sie müssen verstehen, dass dies C ++ - Regeln sind. Sie sind es einfach.

Es ist einfach, eine andere Sprache zu erfinden, beispielsweise C ++ ', mit leicht unterschiedlichen Regeln. In C ++ 'wäre es zulässig, eine nicht konstante Referenz mit einem r-Wert zu erstellen. Hier ist nichts inkonsistent oder unmöglich.

Aber es würde einen riskanten Code ermöglichen, bei dem der Programmierer möglicherweise nicht das bekommt, was er beabsichtigt hat, und C ++ - Designer haben zu Recht beschlossen, dieses Risiko zu vermeiden.

Neugieriger
quelle
0

Verweise sind "versteckte Zeiger" (nicht null) auf Dinge, die sich ändern können (lWerte). Sie können sie nicht als Konstante definieren. Es sollte eine "variable" Sache sein.

BEARBEITEN::

ich denke an

int &x = y;

als fast gleichwertig mit

int* __px = &y;
#define x (*__px)

wo __pxist ein neuer Name, und das #define xfunktioniert nur innerhalb des Blocks, der die xReferenzdeklaration enthält.

Basile Starynkevitch
quelle
1
Warum können Sie, wenn die Referenz ist const:)
Michael Krelin - Hacker
Aber das Beispiel des Plakats war nichtconst
Basile Starynkevitch
Ja, ich meinte Ihren Wortlaut "Referenzen sind Zeiger auf Dinge, die sich ändern können" - es geht um kostenlose Referenzen.
Michael Krelin - Hacker
" Referenzen sind" versteckte Zeiger " " falsch " auf Dinge, die sich ändern können " falsche " Dinge, die sich ändern können (Werte). " Falsch
Neugieriger
@Loki: könntest du mehr erklären?
Basile Starynkevitch