Ist es in C++ besser, als Wert oder als konstante Referenz zu übergeben?

Lesezeit: 11 Minuten

Ist es in C besser als Wert oder als konstante
Matt Pascoe

Ist es in C++ besser, als Wert oder als konstante Referenz zu übergeben?

Ich frage mich, was die bessere Praxis ist. Mir ist klar, dass das Übergeben von konstanten Referenzen für eine bessere Leistung im Programm sorgen sollte, da Sie keine Kopie der Variablen erstellen.

  • verwandt: stackoverflow.com/questions/2139224/…

    – sbi

    26. September 2011 um 21:57 Uhr

Ist es in C besser als Wert oder als konstante
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, doubleusw.), 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:

  1. Wir können das Objekt als Referenz übergeben und dann intern kopieren.
  2. 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.

  • hmmm… Ich bin mir nicht sicher, ob es sich lohnt, an ref vorbeizukommen. Doppel

    – sergtk

    6. November 2008 um 22:11 Uhr

  • Hier hilft wie immer Boost. boost.org/doc/libs/1_37_0/libs/utility/call_traits.htm hat Template-Zeug, um automatisch herauszufinden, ob ein Typ ein eingebauter Typ ist (nützlich für Templates, bei denen Sie das manchmal nicht so einfach wissen können).

    – CesarB

    7. November 2008 um 0:02 Uhr

  • Diese Antwort übersieht einen wichtigen Punkt. Um Slicing zu vermeiden, müssen Sie als Referenz übergeben (const oder anderweitig). Siehe stackoverflow.com/questions/274626/…

    – ChrisN

    15. November 2008 um 18:08 Uhr

  • @chris: stimmt. Ich habe den ganzen Teil des Polymorphismus weggelassen, weil das eine ganz andere Semantik ist. Ich glaube, das OP (semantisch) meinte die Argumentübergabe „durch Wert“. Wenn eine andere Semantik gefragt ist, stellt sich die Frage gar nicht.

    – Konrad Rudolf

    15. November 2008 um 18:43 Uhr

Ist es in C besser als Wert oder als konstante
Johannes Schaub – litb

Bearbeiten: Neuer Artikel von Dave Abrahams auf cpp-next:

Willst du Geschwindigkeit? Wert übergeben.


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 sowieso 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 */
}

  • Ich fand deine Regeln gut, aber ich bin mir nicht sicher, was den ersten Teil betrifft, wo du davon sprichst, es nicht zu bestehen, da ein Schiedsrichter es beschleunigen würde. Ja, sicher, aber etwas nicht als Ref zu übergeben, nur wegen der Optimierung, macht überhaupt keinen Sinn. Wenn Sie das übergebene Stack-Objekt ändern möchten, tun Sie dies mit ref. Wenn Sie dies nicht tun, übergeben Sie es als Wert. Wenn Sie es nicht ändern möchten, übergeben Sie es als const-ref. Die Optimierung, die mit Pass-by-Value einhergeht, sollte keine Rolle spielen, da Sie andere Dinge gewinnen, wenn Sie als Referenz übergeben werden. Ich verstehe das “Geschwindigkeit wollen” nicht? Sice, wenn Sie diese Operation durchführen würden, würden Sie sowieso nach Wert übergeben.

    – chikuba

    2. Mai 2012 um 8:14 Uhr

  • Johannes: Ich geliebt diesen Artikel, als ich ihn las, aber ich war enttäuscht, als ich ihn ausprobierte. Dieser Code ist sowohl auf GCC als auch auf MSVC fehlgeschlagen. Habe ich etwas übersehen oder funktioniert es in der Praxis nicht?

    – Benutzer541686

    14. August 2012 um 21:50 Uhr


  • Ich glaube nicht, dass ich damit einverstanden bin, dass Sie, wenn Sie trotzdem eine Kopie erstellen möchten, sie als Wert übergeben (anstelle von const ref) und sie dann verschieben würden. Betrachten Sie es so, was ist effizienter, eine Kopie und eine Bewegung (Sie können sogar 2 Kopien haben, wenn Sie es weitergeben) oder nur eine Kopie? Ja, es gibt einige Sonderfälle auf beiden Seiten, aber wenn Ihre Daten sowieso nicht verschoben werden können (z. B. ein POD mit Tonnen von Ganzzahlen), sind keine zusätzlichen Kopien erforderlich.

    Benutzer90843

    14. Oktober 2012 um 6:29 Uhr


  • Mehrdad, ich bin mir nicht sicher, was Sie erwartet haben, aber der Code funktioniert wie erwartet

    Benutzer90843

    14. Oktober 2012 um 6:35 Uhr

  • Ich würde die Notwendigkeit des Kopierens in Betracht ziehen, nur um den Compiler davon zu überzeugen, dass sich die Typen nicht als Mangel in der Sprache überschneiden. Ich würde lieber GCCs verwenden __restrict__ (was auch mit Referenzen funktionieren kann) als übermäßige Kopien. Schade, dass Standard-C++ nicht C99 übernommen hat restrict Stichwort.

    – Ruslan

    9. Juli 2017 um 7:31 Uhr


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.

  • Bei nicht-nativen Typen können Sie (je nachdem, wie gut der Compiler den Code optimiert) eine Leistungssteigerung erzielen, indem Sie konstante Verweise anstelle von reinen Verweisen verwenden.

    – ABl.

    6. November 2008 um 22:06 Uhr

1647184215 881 Ist es in C besser als Wert oder als konstante
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:

  1. Ü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)

  2. Ü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.

  3. Ü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.

  4. 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.

  5. Ü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.

  • Addieren std::optional zum Bild und Sie brauchen keine Zeiger mehr.

    – Violette Giraffe

    16. Januar 2020 um 10:46 Uhr

1647184216 45 Ist es in C besser als Wert oder als konstante
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.

  • Addieren std::optional zum Bild und Sie brauchen keine Zeiger mehr.

    – Violette Giraffe

    16. Januar 2020 um 10:46 Uhr

1647184216 789 Ist es in C besser als Wert oder als konstante
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.

  • Wenn Sie die Kopie des Parameters des Aufgerufenen ändern müssen, können Sie eine Kopie im aufgerufenen Code erstellen, anstatt den Wert zu übergeben. IMO sollten Sie die API im Allgemeinen nicht auf der Grundlage eines solchen Implementierungsdetails auswählen: Die Quelle des aufrufenden Codes ist in beiden Fällen dieselbe, der Objektcode jedoch nicht.

    – Steve Jessop

    7. November 2008 um 0:17 Uhr

  • Wenn Sie den Wert übergeben, wird eine Kopie erstellt. Und meiner Meinung nach ist es egal, auf welche Weise Sie eine Kopie erstellen: per Argumentübergabe per Wert oder lokal – das betrifft C ++. Aber vom Design her stimme ich dir zu. Aber ich beschreibe hier nur C++-Funktionen und gehe nicht auf das Design ein.

    – sergtk

    7. November 2008 um 20:17 Uhr

998200cookie-checkIst es in C++ besser, als Wert oder als konstante Referenz zu übergeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy