Wie @ JDługosz in den Kommentaren hervorhebt, gibt Herb in einem anderen (späteren?) Vortrag weitere Ratschläge, siehe ungefähr hier: https://youtu.be/xnqTKD8uD64?t=54m50s .
Sein Rat läuft darauf hinaus, nur Wertparameter für eine Funktion zu verwenden f
, die sogenannte Senkenargumente verwendet, vorausgesetzt, Sie verschieben das Konstrukt aus diesen Senkenargumenten.
Dieser allgemeine Ansatz erhöht nur den Overhead eines Verschiebungskonstruktors sowohl für lvalue- als auch für rvalue-Argumente im Vergleich zu einer optimalen Implementierung von f
auf lvalue- bzw. rvalue-Argumente zugeschnittenen Argumenten. Um zu sehen, warum dies der Fall ist, nehmen wir an, dass f
ein Wertparameter verwendet wird, bei dem T
es sich um einen konstruierbaren Typ zum Kopieren und Verschieben handelt:
void f(T x) {
T y{std::move(x)};
}
Das Aufrufen f
mit einem lvalue-Argument führt dazu, dass ein Kopierkonstruktor zum Konstruieren x
und ein Verschiebungskonstruktor zum Konstruieren aufgerufen werden y
. f
Wenn Sie dagegen mit einem rvalue-Argument aufrufen, wird ein Verschiebungskonstruktor zum Konstruieren x
und ein anderer Verschiebungskonstruktor zum Konstruieren aufgerufen y
.
Im Allgemeinen ist die optimale Implementierung von f
for lvalue-Argumenten wie folgt:
void f(const T& x) {
T y{x};
}
In diesem Fall wird nur ein Kopierkonstruktor zum Konstruieren aufgerufen y
. Die optimale Implementierung von f
for rvalue-Argumenten ist im Allgemeinen wiederum wie folgt:
void f(T&& x) {
T y{std::move(x)};
}
In diesem Fall wird nur ein Verschiebungskonstruktor zum Konstruieren aufgerufen y
.
Ein vernünftiger Kompromiss besteht also darin, einen Wertparameter zu verwenden und einen zusätzlichen Zugkonstruktoraufruf für lvalue- oder rvalue-Argumente in Bezug auf die optimale Implementierung durchzuführen, was auch der Ratschlag in Herbs Vortrag ist.
Wie @ JDługosz in den Kommentaren hervorhob, ist die Übergabe von Werten nur für Funktionen sinnvoll, die ein Objekt aus dem sink-Argument erstellen. Wenn Sie eine Funktion haben f
, die ihr Argument kopiert, hat der Pass-by-Value-Ansatz mehr Overhead als ein allgemeiner Pass-by-Const-Referenzansatz. Der Pass-by-Value-Ansatz für eine Funktion f
, die eine Kopie ihres Parameters beibehält, hat folgende Form:
void f(T x) {
T y{...};
...
y = std::move(x);
}
In diesem Fall gibt es eine Kopierkonstruktion und eine Verschiebungszuweisung für ein lvalue-Argument sowie eine Verschiebungskonstruktion und eine Verschiebungszuweisung für ein rvalue-Argument. Der optimalste Fall für ein lvalue-Argument ist:
void f(const T& x) {
T y{...};
...
y = x;
}
Dies läuft nur auf eine Zuweisung hinaus, die möglicherweise viel billiger ist als der Kopierkonstruktor plus Verschiebungszuweisung, die für den Pass-by-Value-Ansatz erforderlich ist. Der Grund dafür ist, dass die Zuweisung möglicherweise vorhandenen zugewiesenen Speicher in wiederverwendet y
und daher (De-) Zuweisungen verhindert, während der Kopierkonstruktor normalerweise Speicher zuweist.
Für ein rvalue-Argument hat die optimalste Implementierung f
, bei der eine Kopie erhalten bleibt, die folgende Form:
void f(T&& x) {
T y{...};
...
y = std::move(x);
}
Also nur eine Zugzuordnung in diesem Fall. Das Übergeben eines r-Werts an die Version, für f
die eine konstante Referenz erforderlich ist, kostet nur eine Zuweisung anstelle einer Verschiebungszuweisung. Relativ gesehen f
ist es daher vorzuziehen, in diesem Fall eine konstante Referenz als allgemeine Implementierung zu verwenden.
Im Allgemeinen müssen Sie für eine optimale Implementierung eine perfekte Weiterleitung durchführen, wie im Vortrag gezeigt. Der Nachteil ist eine kombinatorische Explosion der Anzahl der erforderlichen Überladungen, abhängig von der Anzahl der Parameter für den f
Fall, dass Sie sich für eine Überlastung der Wertekategorie des Arguments entscheiden. Perfekte Weiterleitung hat den Nachteil, dass sie f
zu einer Vorlagenfunktion wird, die verhindert, dass sie virtuell wird, und zu wesentlich komplexerem Code führt, wenn Sie ihn zu 100% richtig machen möchten (Einzelheiten finden Sie im Vortrag).