2013-04-06 7 views
10

Ich habe einen Typ, der kopierbar ist, aber möglicherweise teuer zu kopieren ist. Ich habe den Move-Konstruktor implementiert und die Zuordnung verschoben. Aber ich habe Performance-Probleme, bei denen Leute vergessen, move() bei der Übergabe nach Wert zu nennen.Sollte ein Typ Nur-Verschieben sein, nur weil das Kopieren teuer sein kann?

Ist es gut C++ 11 Stil, um den Kopierkonstruktor zu entfernen, und stattdessen eine explizite copy() -Methode für die seltenen Fälle, wenn eine Kopie tatsächlich gewünscht ist? Dies ist idiomatisch in anderen Sprachen (Ruby, JavaScript), aber ich weiß nichts von der C++ - Standardbibliothek, die das Kopieren rein für die Performance verbietet. Zum Beispiel ist std :: vector <> kopierbar, während std :: unique_ptr <> und std :: thread aus anderen Gründen nicht kopierbar sind.

+2

Ich stelle mir vor, das Problem mit einer 'copy'-Member-Funktion ist, dass es den gesamten Template-Code bricht, der den Zuweisungsoperator verwendet. – Pubby

Antwort

11

Sollte ein Typ Nur-Verschieben sein, nur weil das Kopieren teuer sein kann?

Nein. Wenn die Semantik Ihre Art ist, so dass es zu kopieren konzeptionell sinnvoll ist, dann ist die richtige Art und Weise zur Verfügung zu stellen Kopieren ist eine Kopie Konstruktor zu implementieren, und gibt dem Anwender die Möglichkeit, Standard-Syntax zu übernehmen für den Aufruf es:

T a; 
T a = b; 

wenn die Leute vergessen, von den Objekten zu bewegen, sie wollen nicht mehr verwenden ... Nun, das ist ihre schlechte:

T c = std::move(a); // I'm doing it right (if I no longer need object a); 
T d = b; // If I don't need b anymore, I'm doing it wrong. 

Und wenn (aus irgendeinem Grund) für einige Funktionen von Ihnen ist es immer wünschenswert, dass der Aufrufer ein Objekt zur Verfügung stellt, von dem man sich bewegen kann, dann lässt die Funktion ein übernehmen R-Wert-Referenz:

void foo(my_class&& obj); 

my_class a; 
foo(a); // ERROR! 
foo(std::move(a)); // OK 
+1

In einigen Kontexten sollten kopierbare Typen nicht über die Kopierkonstruktion kopiert werden: 'virtual T * Clone() const = 0' für Interface-Klassen (oder nicht-finale Implementierungen) drückt Kopierbarkeit aus, kann jedoch nicht als Kopierkonstruktor implementiert werden. Nun können 'wave_ptr ' Wrapper Sie in die Welt der Kopierkonstruktion zurückbringen, aber die 'T'-Klasse selbst ist kopierbar, hat aber keinen Kopierkonstruktor (zumindest keinen öffentlichen). – Yakk

+1

@Yakk: Ich stimme nicht zu. Jeder Standardcontainer kann ziemlich teuer zu kopieren sein, aber es ist kopierbar. Persönlich glaube ich, dass der Vorgang des "Kopierens" eine Semantik hat, die von der Sprache und einer von der Sprache definierten Syntax definiert wird. Diese beiden Dinge sollten übereinstimmen. –

+0

Ich glaube, Sie wollten meine Antwort kommentieren, nicht meinen Kommentar. :) – Yakk

1

Nein. Wenn der Typ kopierbar ist, dann ist der Typ kopierbar. Das bedeutet, dass sein Kopierkonstruktor verfügbar ist und funktioniert. Es bedeutet nicht, dass es eine Elementfunktion gibt, deren Name wie die Zeichen c, o, p und y der Reihe nach aussieht, die "Art von fast einer ähnlichen Sache" macht.

+0

Also, wenn sie die Kopierkonstruktion deaktivieren, dann ist der Typ nicht kopierbar. Es ist kopierbar, duplizierbar oder sonst irgendwie auch verbabel - aber eindeutig nicht kopierbar. Wie ist das ein Problem? – Yakk

+1

@Yakk: Ich stimme Ihrer Klassifizierung zu. Ist es ein Problem? Nun, wer weiß? Hängt davon ab, was Sie _want_ für Ihren Typ möchten. Aber Sie werden es sicherlich nicht mehr kopierbar nennen, und soweit es viele andere Code-Stücke betrifft (z. B. Container), darf Ihr Typ einfach nie kopiert werden, weil Sie es so dekretiert haben. –

+0

Richtig, ich stimme der Analyse nicht zu. Ich schlage vor, meinen Typ nicht kopierbar zu machen, trotz der Tatsache, dass es eindeutig ein Werttyp ist. –

4

Ich würde die Klasse als nicht kopierbar in Signatur behandeln, wenn die Kopie ausreichend teuer ist. Semantisch gesehen sind Dinge nur dann kopierbar, wenn man sie haben will, und eine teure Kopie ist ein guter Grund, sich für "nein, nicht kopierbar" zu entscheiden.

Die Fähigkeit, etwas zu kopieren, bedeutet nicht, dass es in einem kopierbaren Typ implementiert werden muss. Der Implementierer dieses Typs entscheidet, ob er semantisch kopierbar sein soll.

Ich würde nicht die Operation nennen, die eine teure Kopie "Kopie" produziert, sondern eher "klonen" oder "duplizieren".

Für einen Weg, dies tun könnte:

#include <utility> 

template<typename T> 
struct DoCopy { 
    T const& t; 
    DoCopy(T const& t_):t(t_) {} 
}; 
template<typename T> 
DoCopy<T> do_copy(T const& t) { 
    return t; 
} 
struct Foo { 
    struct ExpensiveToCopy { 
    int _[100000000]; 
    }; 
    ExpensiveToCopy* data; 
    Foo():data(new ExpensiveToCopy()) {} 
    ~Foo(){ delete data; } 
    Foo(Foo&& o):data(o.data) { o.data = nullptr; } 
    Foo& operator=(Foo&& o) { data=o.data; o.data=nullptr; return *this; } 
    Foo& operator=(DoCopy<Foo> o) { 
    delete data; 
    if (o.t.data) { 
     data=new ExpensiveToCopy(*o.t.data); 
    } else { 
     data=new ExpensiveToCopy(); 
    } 
    return *this; 
    } 
    Foo(DoCopy<Foo> cp):data(cp.t.data?new ExpensiveToCopy(*cp.t.data):new ExpensiveToCopy()) {}; 
}; 
int main() { 
    Foo one; 
    // Foo two = one; // illegal 
    Foo three = std::move(one); // legal 
    Foo four; 
    Foo five = do_copy(three); 
    four = std::move(three); 
    five = do_copy(four); 
} 

Diese auf die Art und Weise ein wenig ähnlich ist Ihnen std::move wie Semantik vor der Existenz von rvalue Referenzen, mit ähnlichen Nachteilen auf solche Techniken geschrieben haben könnte, dass nämlich Die Sprache selbst hat keine Ahnung, was für Spielereien du bist.

Es hat den Vorteil, dass die Syntax der oben do_copy auf die Syntax von std::move ähnlich ist, und es erlaubt Ihnen traditionelle Ausdrücke zu verwenden, ohne eine Kopie eines anderen Variablen usw.

triviale Instanzen Foo dann konstruieren zu schaffen, die

Wenn die Situationen, in denen wir sie als kopierbar behandeln möchten, üblich sind (wenn sie vermieden werden sollen), schreibe ich einen Copy-Wrapper um die Klasse, die über die duplicate-Methode Bescheid weiß.

+1

Ich würde den Benutzer meiner Bibliothek der Richter sein lassen. –

+0

Ich mag diese Lösung. Ich habe etwas Ähnliches für kopierbare Hierarchien gemacht, und die move-like-Syntax ist eine nette Geste. –

Verwandte Themen