
Matt Pascoe
Ist es in C++ besser, als Wert oder als Referenz an Konstante zu übergeben?
Ich frage mich, was die bessere Praxis ist. Mir ist klar, dass die Weitergabe von Verweisen auf Konstante für eine bessere Leistung im Programm sorgen sollte, da Sie keine Kopie der Variablen erstellen.

Konrad Rudolf
Früher war es eine allgemein empfohlene Best Practice1 zu Verwenden Sie Pass by const ref for alle Artenaußer für eingebaute Typen (char
, int
, double
usw.), für Iteratoren und für Funktionsobjekte (Lambdas, Klassen abgeleitet von std::*_function
).
Dies galt insbesondere vor der Existenz von Semantik bewegen. Der Grund ist einfach: Bei Wertübergabe musste eine Kopie des Objekts angefertigt werden und das ist, außer bei sehr kleinen Objekten, immer teurer als eine Referenzübergabe.
Mit C++11 haben wir gewonnen Semantik bewegen. Kurz gesagt erlaubt die Bewegungssemantik, dass in einigen Fällen ein Objekt „nach Wert“ übergeben werden kann, ohne es zu kopieren. Dies ist insbesondere dann der Fall, wenn es sich bei dem Objekt, an dem Sie vorbeifahren, um einen handelt rwert.
Das Verschieben eines Objekts an sich ist immer noch mindestens so teuer wie das Übergeben von Referenzen. In vielen Fällen kopiert eine Funktion jedoch intern ein Objekt, dh es dauert Eigentum des Arguments.2
In diesen Situationen haben wir den folgenden (vereinfachten) Kompromiss:
- Wir können das Objekt als Referenz übergeben und dann intern kopieren.
- Wir können das Objekt als Wert übergeben.
„Pass by value“ bewirkt immer noch, dass das Objekt kopiert wird, es sei denn, das Objekt ist ein Rvalue. Bei einem rvalue kann das Objekt stattdessen verschoben werden, sodass der zweite Fall plötzlich nicht mehr „kopieren, dann verschieben“, sondern „verschieben, dann (möglicherweise) erneut verschieben“ lautet.
Bei großen Objekten, die geeignete Bewegungskonstruktoren implementieren (wie Vektoren, Zeichenfolgen …), ist der zweite Fall dann erheblich effizienter als die erste. Daher ist es empfehlenswert Verwenden Sie Wertübergabe, wenn die Funktion den Besitz des Arguments übernimmt und wenn der Objekttyp ein effizientes Verschieben unterstützt.
Eine historische Anmerkung:
Tatsächlich sollte jeder moderne Compiler in der Lage sein, herauszufinden, wann das Übergeben von Werten teuer ist, und den Aufruf implizit konvertieren, um nach Möglichkeit eine konstante Referenz zu verwenden.
In der Theorie. In der Praxis können Compiler dies nicht immer ändern, ohne die binäre Schnittstelle der Funktion zu beschädigen. In einigen Sonderfällen (wenn die Funktion eingebettet ist) wird die Kopie tatsächlich entfernt, wenn der Compiler herausfinden kann, dass das ursprüngliche Objekt durch die Aktionen in der Funktion nicht geändert wird.
Aber im Allgemeinen kann der Compiler dies nicht bestimmen, und das Aufkommen der Bewegungssemantik in C++ hat diese Optimierung viel weniger relevant gemacht.
1 B. bei Scott Meyers, Effektives C++.
2 Dies gilt besonders oft für Objektkonstruktoren, die Argumente annehmen und sie intern speichern können, damit sie Teil des Zustands des konstruierten Objekts sind.

Johannes Schaub – litb
Bearbeiten: Neuer Artikel von Dave Abrahams auf cpp-next:
Die Wertübergabe für Strukturen, bei denen das Kopieren billig ist, hat den zusätzlichen Vorteil, dass der Compiler davon ausgehen kann, dass die Objekte keinen Alias haben (nicht dieselben Objekte sind). Mit Pass-by-Reference kann der Compiler das nicht immer annehmen. Einfaches Beispiel:
foo * f;
void bar(foo g) {
g.i = 10;
f->i = 2;
g.i += 5;
}
der Compiler kann es optimieren
g.i = 15;
f->i = 2;
da es weiß, dass f und g nicht denselben Ort haben. wenn g eine Referenz wäre (foo &), hätte der Compiler das nicht annehmen können. da gi dann von f->i aliasiert werden könnte und einen Wert von 7 haben müsste, müsste der Compiler also den neuen Wert von gi erneut aus dem Speicher abrufen.
Für praktischere Regeln finden Sie hier ein gutes Regelwerk in Konstruktoren verschieben Artikel (sehr empfehlenswerte Lektüre).
- Wenn die Funktion beabsichtigt, das Argument als Nebeneffekt zu ändern, nehmen Sie es als Nicht-Konstanten-Referenz.
- Wenn die Funktion ihr Argument nicht ändert und das Argument vom primitiven Typ ist, nehmen Sie es als Wert.
- Nehmen Sie es andernfalls als const-Referenz, außer in den folgenden Fällen
- Wenn die Funktion dann sowieso eine Kopie der const-Referenz erstellen müsste, nehmen Sie sie als Wert.
“Primitiv” oben bedeutet im Grunde genommen kleine Datentypen, die einige Bytes lang sind und nicht polymorph (Iteratoren, Funktionsobjekte usw.) oder teuer zu kopieren sind. In diesem Papier gibt es eine andere Regel. Die Idee ist, dass man manchmal eine Kopie machen möchte (falls das Argument nicht geändert werden kann) und manchmal nicht (falls man das Argument selbst in der Funktion verwenden möchte, wenn das Argument ohnehin ein temporäres war , zum Beispiel). Das Papier erklärt ausführlich, wie das geschehen kann. In C++1x kann diese Technik nativ mit Sprachunterstützung verwendet werden. Bis dahin würde ich mich an die oben genannten Regeln halten.
Beispiele: Um einen String in Großbuchstaben zu schreiben und die Großbuchstabenversion zurückzugeben, sollte man immer als Wert übergeben: Man muss sowieso eine Kopie davon nehmen (man könnte die const-Referenz nicht direkt ändern) – also besser so transparent wie möglich machen den Aufrufer und machen Sie diese Kopie frühzeitig, damit der Aufrufer so viel wie möglich optimieren kann – wie in diesem Artikel beschrieben:
my::string uppercase(my::string s) { /* change s and return it */ }
Wenn Sie den Parameter jedoch nicht ändern müssen, verwenden Sie ihn als Referenz auf const:
bool all_uppercase(my::string const& s) {
/* check to see whether any character is uppercase */
}
Wenn der Zweck des Parameters jedoch darin besteht, etwas in das Argument zu schreiben, übergeben Sie es als nicht konstante Referenz
bool try_parse(T text, my::string &out) {
/* try to parse, write result into out */
}
Kommt auf den Typ an. Sie fügen den kleinen Overhead hinzu, der durch das Erstellen einer Referenz und Dereferenzierung entsteht. Bei Typen mit einer Größe gleich oder kleiner als Zeiger, die den standardmäßigen Kopierctor verwenden, wäre es wahrscheinlich schneller, den Wert zu übergeben.

Torlack
Wie gesagt, es kommt auf den Typ an. Bei integrierten Datentypen ist es am besten, den Wert zu übergeben. Sogar einige sehr kleine Strukturen, wie z. B. ein Paar Ints, können eine bessere Leistung erbringen, indem sie als Wert übergeben werden.
Hier ist ein Beispiel, nehmen Sie an, Sie haben einen ganzzahligen Wert und Sie möchten ihn an eine andere Routine übergeben. Wenn dieser Wert für die Speicherung in einem Register optimiert wurde, muss er, wenn Sie ihn als Referenz übergeben möchten, zuerst im Speicher gespeichert und dann ein Zeiger auf diesen Speicher auf dem Stapel abgelegt werden, um den Aufruf auszuführen. Wenn es als Wert übergeben wurde, ist alles, was erforderlich ist, das Register, das auf den Stapel geschoben wird. (Die Details sind etwas komplizierter als bei unterschiedlichen Aufrufsystemen und CPUs).
Wenn Sie mit Template-Programmierung arbeiten, sind Sie normalerweise gezwungen, immer const ref zu übergeben, da Sie die übergebenen Typen nicht kennen. Strafen für die Übergabe von etwas Schlechtem als Wert sind viel schlimmer als die Strafen für die Übergabe eines eingebauten Typs von const ref.
So arbeite ich normalerweise, wenn ich die Schnittstelle einer Nicht-Vorlagenfunktion entwerfe:
-
Übergeben Sie den Wert, wenn die Funktion den Parameter nicht ändern möchte und der Wert billig zu kopieren ist (int, double, float, char, bool usw.). Beachten Sie, dass std::string, std::vector und der Rest der Container in der Standardbibliothek sind NICHT)
-
Übergeben Sie den konstanten Zeiger, wenn das Kopieren des Werts teuer ist und die Funktion den Wert, auf den gezeigt wird, nicht ändern möchte und NULL ein Wert ist, den die Funktion verarbeitet.
-
Übergeben Sie einen nicht konstanten Zeiger, wenn das Kopieren des Werts teuer ist und die Funktion den Wert ändern möchte, auf den gezeigt wird, und NULL ein Wert ist, den die Funktion verarbeitet.
-
Pass by const reference, wenn das Kopieren des Werts teuer ist und die Funktion den Wert, auf den verwiesen wird, nicht ändern möchte und NULL kein gültiger Wert wäre, wenn stattdessen ein Zeiger verwendet würde.
-
Übergeben Sie eine nicht konstante Referenz, wenn das Kopieren des Werts teuer ist und die Funktion den Wert ändern möchte, auf den verwiesen wird, und NULL kein gültiger Wert wäre, wenn stattdessen ein Zeiger verwendet würde.

GeekyMonkey
Klingt, als hättest du deine Antwort bekommen. Die Wertübergabe ist teuer, gibt Ihnen aber eine Kopie, mit der Sie arbeiten können, wenn Sie sie brauchen.

sergtk
In der Regel ist das Übergeben von const-Referenzen besser. Aber wenn Sie Ihr Funktionsargument lokal ändern müssen, sollten Sie besser die Wertübergabe verwenden. Für einige Grundtypen ist die Leistung im Allgemeinen sowohl für die Wertübergabe als auch für die Referenzübergabe gleich. Tatsächlich wird die Referenz intern durch den Zeiger dargestellt, weshalb Sie beispielsweise erwarten können, dass für den Zeiger beide Übergaben in Bezug auf die Leistung gleich sind, oder sogar die Wertübergabe aufgrund unnötiger Dereferenzierung schneller sein kann.
10196400cookie-checkIst es in C++ besser, als Wert oder als Referenz an Konstante zu übergeben?yes
verwandt: stackoverflow.com/questions/2139224/…
– sbi
26. September 2011 um 21:57 Uhr