6 Stimmen

Übergabe als Wert oder Referenz an einen C++-Konstruktor, der eine Kopie speichern muss?

Sollte ein (impliziter oder expliziter) C++-Wert-Konstruktor seine(n) Parameter als Wert oder als Verweis auf eine Konstante akzeptieren, wenn er eine Kopie des/der Argumente(s) in seinem Objekt speichern muss, egal wie?

Hier ist das kürzeste Beispiel, das mir einfällt:

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

Die Idee dabei ist, dass ich die Aufrufe des Kopierkonstruktors von bar minimieren möchte, wenn ein foo-Objekt erstellt wird, und zwar auf jede der verschiedenen Arten, auf die ein foo-Objekt erstellt werden könnte.

Bitte beachten Sie, dass ich ein wenig über Copy Elision und (Named) Return Value Optimization weiß, und ich habe gelesen "Schnelligkeit gewünscht? Pass by Value" Ich glaube jedoch nicht, dass der Artikel direkt auf diesen Anwendungsfall eingeht.

Bearbeiten: Ich sollte genauer sein.

Gehen Sie davon aus, dass ich nicht wissen kann, welche sizeof(bar) oder ob bar ist ein grundlegender, eingebauter Typ ( bar kann ein Vorlagenparameter sein, und foo kann eine Klassenvorlage statt einer Klasse sein). Gehen Sie auch nicht davon aus, dass foo Konstruktor kann inlined werden (oder bar und die der anderen). Gehen Sie davon aus, dass ich zumindest einen Compiler verwenden könnte, der RVO implementiert.

Was ich möchte, ist, dass es eine Möglichkeit gibt (angesichts der Compiler-Optimierungen), dass ein Aufruf wie dieser keine Aufrufe an bar Kopierkonstruktor (auch bei der Ausführung von _b(b) en foo der Initialisierungsliste):

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

Gibt es irgendeine Möglichkeit (angesichts des C++98-Standards), dass dies möglich ist, und wenn ja, ist es mehr oder weniger wahrscheinlich, dass es funktioniert, wenn foo seinen Parameter per Referenz-zu-Konst statt per Wert akzeptiert?

5voto

Doug T. Punkte 61739

Wenn alle Dinge gleich sind, übergebe ich const& für ausreichend komplexe Klassen und value für POD und einfache Objekte.

Erläuterung der Vor- und Nachteile der Übergabe per Konst-Referenz anstelle der traditionellen Übergabe per Wert

Positiv:

  • Vermeidet eine Kopie (großes Plus für Objekte mit einer teuren Kopie)
  • Nur-Lese-Zugriff

Negativ:

  • Jemand kann die Konstante aus dem Verweis entfernen, wenn er das wirklich will.

Wichtiger als die positiven Aspekte ist, dass Sie ausdrücklich kontrollieren, wann die Kopie erfolgt (in Ihrem Fall bei der Übergabe der Initialisierung von _b in Ihrer Initialisierungsliste). Wenn man über die negativen Aspekte nachdenkt... Ich stimme zu, dass es ein Risiko ist. Ich denke, dass fast alle guten Programmierer ein ungutes Gefühl bei der Verwendung von const_cast haben. Außerdem kann man pflichtbewusst nach const_cast suchen und Tomaten auf die Person werfen, die das const aus dem Argument castet. Aber hey, man weiß ja nie, und wer hat schon Zeit, Code wie ein Falke zu beobachten :)?

Meiner subjektiven Meinung nach überwiegt bei ausreichend komplexen Klassen und in Umgebungen, in denen die Leistung eine Rolle spielt, der Nutzen der Vermeidung des Kopierkonstruktors das Risiko. Bei wirklich dummen und POD-Klassen neige ich jedoch dazu, Kopien der Daten zu erstellen und nach Wert zu übergeben.

4voto

Alexey Malistov Punkte 25541

Siehe dies Frage .

Utilisez const T & arg si sizeof(T)>sizeof(void*) und verwenden T arg si sizeof(T) <= sizeof(void*) . Alle Grundtypen sollten die Ausnahme von dieser Regel sein

2voto

Kristopher Johnson Punkte 78933

Stilistisch würde ich sagen, dass die Übergabe durch Verweis der bessere Weg ist.

Wenn die Leistung wirklich wichtig ist, sollten Sie nicht raten. Messen Sie sie.

2voto

Josef Grahn Punkte 1535

Ich habe nicht überprüft, was der Standard sagt, aber durch das Ausprobieren dieser empirisch kann ich Ihnen sagen, dass GCC nicht die Kopie weg optimieren, unabhängig davon, ob der Konstruktor das Argument durch Wert oder durch const-Referenz akzeptiert.

Wenn der Konstruktor jedoch eine Konst-Referenz annimmt, kann er eine unnötige Kopie vermeiden, wenn Sie die foo aus einer bestehenden bar Objekt.

Zusammengefasst:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

Ich bin zwar kein Compiler-Experte, aber ich denke, es macht Sinn, dass er einen Rückgabewert im Allgemeinen nicht an eine beliebige Stelle (z. B. das Member-Feld der foo Objekt). Stattdessen muss sich das Ziel oben auf dem Stapel befinden.

2voto

Fred Punkte 152

In C++98 und C++03 sollten Sie const& bar und dann kopieren. In C++0x sollten Sie bar und dann eine Bewegung durchführen (sofern bar hat einen Move-Konstruktor).

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

Wenn Sie foo mit einem l-Wert-Parameter konstruieren, wird der Kopierkonstruktor aufgerufen, um eine Kopie zu erstellen b und diese Kopie wird nach _b . Wenn Sie foo mit einem rvalue-Parameter konstruieren, bar wird der Konstruktor von move aufgerufen, um in b und dann wird es wieder in _b .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X