2016-01-19 7 views
6

ich in einer Datei arbeite, die eine bestimmte Funktion mit vielen Überlastungen hat, etwa so:Sollte meine generische Vorlage T oder T && verwenden?

inline X f(ScriptWrappable* impl, Y y, Z z) { ... } 
inline X f(Node* impl, Y y, Z z) { ... } 
inline X f(RawPtr<T> impl, Y y, Z z) { ... } 
inline X f(const RefPtr<T>& impl, Y y, Z z) { ... } 
inline X f(ScriptWrappable* impl, Y y, Z z) { ... } 
inline X f(const String& impl, Y y, Z z) { ... } 
inline X f(int64_t impl, Y y, Z z) { ... } 
template<typename T, size_t capacity> inline X f(const Vector<T, capacity>& impl, Y y, Z z) { ... } 

Ich versuche, eine neue Überlastung hinzuzufügen, die nur einen Parameter, wie so erfordert:

template<typename T> inline X f(T impl, W w) { 
    return f(impl, w->getY(), w->getZ()); 
} 

Ich verwende Vorlagen, so dass alle oben genannten Variationen automatisch mit meiner neuen Zwei-Parameter-Version funktionieren.

Allerdings wurde ich während der Überprüfung gefragt, "Ist T&& besser für die Vermeidung von Kopie?". Das heißt, sollte ich stattdessen tun

Ich weiß nicht wirklich die Antwort auf diese Frage. Ich dachte, ich verstehe universelle Referenzen, aber ich weiß nicht, wann sie eine gute Idee sind oder nicht. Was wären die Konsequenzen in meiner Situation, eine über die andere zu wählen?

Wenn ich raten müsste, würde ich sagen, dass da die ursprünglichen Typen, für die T stehen kann, alle einfach zu kopieren sind (Primitive, Referenzen oder Zeiger) T & & wird nicht viel Nutzen bringen. Aber ich bin immer noch neugierig, wenn z.B. Alle Typen können an die Version T & & übergeben werden, die nicht an die T-Version übergeben werden konnte oder umgekehrt.

+0

Wenn der Anrufer sich bewegen möchte, sollte er nur das Argument "std :: move" bewegen. Indem Sie 'T &&' verwenden, zwingen Sie ** ihn dazu. Wenn Sie nur 'T' verwenden, geben Sie ihm die Wahl. –

+4

dies ist nicht wahr, wenn Vorlagen in abgeleiteten Kontext verwendet –

+0

Ah, wusste das nicht. Danke für die Klarstellung. –

Antwort

8

Sie sollten Ihre Funktion wie folgt schreiben:

template<typename T> inline X f(T&& impl, W w) { 
    return f(std::forward<T>(impl), w->getY(), w->getZ()); 
} 

Dies ist ideal Forwarding genannt. Der Typ impl wird genau so aussehen, als hätten Sie f direkt aufgerufen. Es wird nicht unbedingt eine r-Wert-Referenz sein.

+0

Danke! Es klingt wie eine perfekte Weiterleitung, die besser zu meinen Absichten passt. Ich hoffe immer noch zu verstehen, was anders passieren würde, wenn ich die anderen Alternativen gewählt hätte, aber die Suche nach "perfekter Weiterleitung" gibt mir Seiten wie http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal -Referenzen-in-c/die scheinen zu helfen. – Domenic

+0

Werfen Sie einen Blick auf die Antwort von Richard Hodges. –

+0

@TobiasBrandt, Es sei denn, f soll Besitz von impl übernehmen, warum nicht const T & benutzen. –

3

Die Antwort ist ein wenig komplex, weil es ein Verständnis der Regeln des Compilers in Bezug auf Vorlagen erfordert.

In der folgenden Erklärung:

template<class T> void func(T&& t); 

T in abgeleiteten Kontext und als Ergebnis ausgewertet wird, wird durch den Compiler entweder als r-Referenzwert oder einen L-Bezugswert behandelt werden, was auch immer angebracht ist. Gekoppelt mit der Verwendung von std::forward (Hinweis: in diesem Fall nichtstd::move) ergibt dies eine perfekte Weiterleitung und ist optimal effizient.

Dies ist jedoch nicht in hergeleitete Kontext ausgewertet:

template<class T> 
struct X { 
    void foo(T&& t); 
}; 

In diesem Fall ist foo in der Tat einen r-Referenzwert anspruchsvoll.

Weder ist dies abgeleitete Kontext:

template<class T> 
void foo(std::vector<T>&& v); 

In diesen beiden Fällen sollen Sie std::move verwenden.

+0

Es ist nicht die Auswertung von "T" in einem abgeleiteten Kontext, die es entweder zu einem r-Wert oder zu einem l-Wert macht. 'typedef int & T; typedef T && X;' hat keinen Kontext abgeleitet, aber 'X' ist vom Typ' int & '. Ersetzen durch 'typedef int T;' und 'X' wird vom Typ' int && '. Es gibt keine Magie, die Deduktion mit einbezieht, die 'T &&' entweder eine rvalue oder lvalue-Referenz macht; das sind die Regeln für das Kollabieren von Referenzen. Die Deduktionsregeln führen dazu, dass "T" in "T &&" auf bestimmte Weise abgeleitet wird, um zu ermöglichen, dass die Regeln entweder eine l oder r Wertreferenz erzeugen, aber das ist ein echter Unterschied. – Yakk

+0

Verzeihen Sie, wenn ich den Wortlaut falsch habe. Was ist die korrekte Form von Wörtern? –

+0

Wer kann, post. Diejenigen, die das nicht können, kritisieren die Beiträge anderer. :) Die Auswahl der Wörter, die benötigt werden, um über den Vorlagentyp-Abzug von Funktionsargumenten und Weiterleitungsreferenzen zu sprechen, ist * schwer *. – Yakk

Verwandte Themen