Frage

Wenn ich kompilieren den folgenden Code g++ mit

class A {};

void foo(A&) {}

int main()
{
  foo(A());
  return 0;
}

Ich bekomme die folgende Fehlermeldung:

> g++ test.cpp -o test     
test.cpp: In function ‘int main()’:
test.cpp:10: error: invalid initialization of non-const reference of type ‘A&’ from a temporary of type ‘A’
test.cpp:6: error: in passing argument 1 of ‘void foo(A&)’

Nach einigem Nachdenken, diese Fehler machen viel Sinn für mich. A() ist nur ein temporärer Wert, nicht belegbare Lage auf dem Stapel, so wäre es nicht eine Adresse zu haben scheint. Wenn es keine Adresse haben, dann kann ich nicht einen Verweis auf sie halten. Okay, gut.

Aber warten Sie! Wenn ich den folgenden Konvertierungsoperator der Klasse A hinzufügen

class A
{
public:
  operator A&() { return *this; }
};

dann ist alles gut! Meine Frage ist, ob dies auch nur entfernt sicher. Was genau macht this Punkt, wenn A() als temporärer Wert aufgebaut ist?

Ich bin etwas Vertrauen durch die Tatsache gegeben, dass

void foo(const A&) {}

können temporäre Werte akzeptieren nach g++ und allen anderen Compilern ich benutzt habe. Das const Schlüsselwort kann immer weg geworfen werden, so würde es mich überraschen, wenn es tatsächliche semantische Unterschiede zwischen einem const A& Parameter und einem A& Parameter waren. Also ich denke, das ist eine andere Art und Weise meine Frage zu stellen: warum ein const Referenz betrachtet auf einen temporären Wert ist sicher, durch den Compiler, während ein nicht-const Referenz ist nicht

?
War es hilfreich?

Lösung

Es ist nicht, dass eine Adresse kann nicht genommen werden (der Compiler immer konnte, um sie auf den Stapel geschoben, die es mit ref-to-konst der Fall ist), es ist eine Frage von Programmierern Absicht. Mit einer Schnittstelle, die ein A & nimmt, ist es zu sagen: „Ich werde ändern, was in diesem Parameter ist, so dass Sie nach dem Funktionsaufruf lesen können“. Wenn Sie ihm eine vorübergehende, dann die Sache übergeben es „modifizierte“ existiert nicht nach der Funktion. Dies ist (wahrscheinlich) ein Programmierfehler, so dass es nicht erlaubt ist. Betrachten wir zum Beispiel:

void plus_one(int & x) { ++x; }

int main() {
   int x = 2;
   float f = 10.0;

   plus_one(x); plus_one(f);

   cout << x << endl << f << endl;
}

Dies gilt nicht kompilieren, aber wenn Provisorien zu einem ref-zu-nicht-const binden könnten, wäre es kompilieren, aber überraschende Ergebnisse. In plus_one (f) würde f implizit auf eine temporäre int umgewandelt wird, würde plus_one die temp und erhöht es, den zugrunde liegenden Schwimmer f unberührt bleiben. Wenn plus_one zurückgekehrt, hätte es keine Wirkung hatte. Dies ist fast sicher nicht das, was der Programmierer gedacht.


Die Regel tut gelegentlich vermasseln. Ein gängiges Beispiel (beschrieben hier ) , versucht, eine Datei zu öffnen, drucken sie etwas, und es zu schließen. Sie würden wollen in der Lage sein zu tun:

ofstream("bar.t") << "flah";

Sie können aber nicht, weil Operator << nimmt einen ref-zu-nicht-const. Ihre Optionen sind brechen sie in zwei Linien oder eine Methode aufrufen ein ref-zu-nicht-const Rückkehr:

ofstream("bar.t").flush() << "flah";

Andere Tipps

Wenn Sie einen r-Wert auf eine konstante Referenz zuweisen, werden Sie garantiert, dass die temporäre nicht zerstört werden, bis die Referenz zerstört wird. Wenn Sie auf eine nicht konstante Referenz zuweisen, wird keine solche Garantie.

int main()
{
   const A& a2= A(); // this is fine, and the temporary will last until the end of the current scope.
   A& a1 = A(); // You can't do this.
}

Sie können nicht sicher const-ness nolens volens wegzuwerfen und erwarten, dass die Dinge funktionieren. Es gibt verschiedene Semantik auf const und nicht-const Referenzen.

Gotcha, dass einige Leute in ausführen können: die MSVC-Compiler (Visual Studio-Compiler, mit Visual Studio 2008 überprüft) wird ohne Probleme diesen Code kompilieren. Wir hatten für Funktionen in einem Projekt dieses Paradigma wurde verwenden, die in der Regel ein Argument hat (einen Teil der Daten zu verdauen), aber manchmal wollte die Brocken und Ertrag Ergebnisse zurück an den Anrufer suchen. Der andere Modus, indem man drei Argumente aktiviert wurde --- waren das zweite Argument die Informationen auf (default Bezugnahme auf leere Zeichenkette) zu suchen, und das dritte Argument war für die Rückkehrdaten (default Bezugnahme auf leere Liste des gewünschten Typs).

arbeitete Dieses Paradigma in Visual Studio 2005 und 2008, und wir hatten es Refactoring, so dass die Liste erstellt wurde und zurück statt im Besitz-by-Anrufer-and-mutiert mit g ++ zu kompilieren.

Wenn es ein Weg, um den Compiler-Schalter entweder nicht zulassen, diese Art von Verhalten in MSVC oder lassen Sie es in g ++ zu setzen, wäre ich aufgeregt zu wissen; die Permissivität des MSVC Compiler / restrictiveness des g ++ Compiler fügt Komplikationen zu portieren Code.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top