2016-03-25 11 views
5

Die Unterscheidung zwischen std::move und std::forward ist gut bekannt, wir verwenden letztere, um die Wertkategorie eines weitergeleiteten Objekts zu erhalten, und erstere, um auf rvalue zu referenzieren, um die Bewegungssemantik zu ermöglichen.Warum nicht immer std :: forward verwenden?

In wirksamen modernen C++ existiert eine Richtlinie, dass die Staaten

Verwendung std::move auf rvalue Referenzen, std::forward auf Universal-Referenzen.

Doch im folgende Szenario (und Szenarien, in denen wir nicht wollen, Wertkategorie ändern),

template <class T> 
void f(vector<T>&& a) 
{ 
    some_func(std::move(a)); 
} 

wo a keine Forwarding Referenz ist aber eine einfache rvalue Referenz, wouldn‘ t es genau das gleiche zu tun, das folgende?

template <class T> 
void f(vector<T>&& a) 
{ 
    some_func(std::forward<decltype(a)>(a)); 
} 

Da dies kann leicht in einem Makro wie dieser, verkapselt werden, wie so

#define FWD(arg) std::forward<decltype(arg)>(arg) 

ist es nicht praktisch, immer diese Makrodefinition zu verwenden?

void f(vector<T>&& a) 
{ 
    some_func(FWD(a)); 
} 

Sind die beiden Schreibweisen nicht exakt gleichwertig?

+3

Ich habe Probleme beim Anzeigen der Makro-Lösung als bequemer als das Schreiben 'move()' ...aber das erscheint mir als eine meinungsbezogene Frage. Beide machen das gleiche – Barry

+3

aber manchmal ist das Argument eine reguläre Referenz und Sie möchten den Vektor verschieben. weiterleiten mit der Weiterleitung erreicht das nicht –

+0

@Barry Ich wollte eine Bestätigung, dass sie gleichwertig sind, thnx dafür. Ich möchte widersprechen, ob sie auf Meinung basieren, einfach befürworten auf einen Programmierstil nicht Voreingenommenheit, könnte man sehr gute Gründe haben, es zu tun (und in der Lage sein, sie zu erarbeiten) –

Antwort

6

Eh. Fraglich. In Ihrem speziellen Beispiel ist es gleichwertig. Aber ich würde es mir nicht zur Gewohnheit machen. Ein Grund dafür ist, dass Sie eine semantische Unterscheidung zwischen Weiterleiten und Verschieben wünschen. Ein weiterer Grund ist, weil Sie eine konsistente API haben müssen MOV zusätzlich zu FWD, und das sieht wirklich schlecht aus und tut nichts. Noch wichtiger ist jedoch, dass Ihr Code unerwartet fehlschlagen kann.

Betrachten Sie den folgenden Code ein:

#include <iostream> 

using namespace std; 

#define FWD(arg) std::forward<decltype(arg)>(arg) 

struct S { 
    S() { cout << "S()\n"; } 
    S(const S&) { cout << "S(const S&)\n"; } 
    S(S&&) { cout << "S(S&&)\n"; } 
}; 

void some_func(S) {} 

void f(S&& s) 
{ 
    some_func(FWD(s)); 
} 

int main() 
{ 
    f(S{}); 
} 

Dies gibt

S()
S (S & &)

Allerdings, wenn ich FWD nur die Änderung Linie, um ein anderes (scheinbar optionales) Elternpaar zu haben thesen, wie folgt aus:

void f(S&& s) 
{ 
    some_func(FWD((s))); 
} 

Jetzt bekommen wir

S()
S (const S &)

Und das liegt daran, jetzt sind wir decltype auf eine mit Ausdruck, der zu einer lvalue-Referenz ausgewertet wird.

+0

Sie machen gute Punkte. Aber ich stimme der "scheinbar optionalen" Charakterisierung für die Klammern nicht zu. Wenn man mit 'declltype' arbeitet, sollte man [this stuff] (http://stackoverflow.com/a/17242295/4224575) wissen und das ist ein Fehler, der sogar in' std :: forward

+2

@Lorah Ja, aber es ist ein Makro - Sie können nicht sofort sehen, dass es 'declltype' verwendet. – Barry

+0

@Barry Ich denke, Sie würden nicht sehen, was es tut, wenn es auch eine Funktion war, Sie müssen immer den Code lesen; Ich begreife den "Mangel an Transparenz", der mit Makros verbunden ist, meistens als "seltsame Nebenwirkungen oder Mutationen des Verhaltens", die selbst beim Lesen des Codes nicht offensichtlich sind (wie zB einen Ausdruck zweimal auszuwerten usw.). Ich verwende ein Makro nicht aus Gründen wie 'FWD', d. H. Textersetzung (keine doppelte Bewertung, keine zufälligen Datenstrukturen, kein versteckter Kontrollfluss). Könnten Sie sich bitte [this] (http://stackoverflow.com/q/36230428/4224575) ansehen? (es ist off Thema, aber ich könnte etwas Hilfe verwenden) –

Verwandte Themen