2014-10-21 9 views
10

Nach ein wenig Forschung, sehe ich, dass C++11 has a defect mit Allokatoren, die den Typ erfordern, beweglich/kopierbar sein. Ich bin mir sicher, dass dies die Ursache dieses Problems ist, aber ich bin verwirrt über das Verhalten zwischen gelöschter und nicht deklarierter Verschiebungssemantik.Warum verursacht gelöschte move-Semantik Probleme mit std :: vector?

Ich habe den folgenden Code, der auf beiden MSVC12 und Clang kompiliert fehlschlägt:

xmemory0(600): error C2280: 'Copyable::Copyable(Copyable &&)' : attempting to reference a deleted function

Und auf Clang (live sample):

#include <vector> 

class Copyable 
{ 
public: 
    Copyable() = default; 

    Copyable(Copyable const& other) 
     : m_int(other.m_int) 
    {} 

    Copyable& operator= (Copyable const& other) 
    { 
     m_int = other.m_int; 
     return *this; 
    } 

    Copyable(Copyable&&) = delete; 
    Copyable& operator= (Copyable&&) = delete; 

private: 
    int m_int = 100; 
}; 

int main() 
{ 
    std::vector<Copyable> objects; 
    objects.push_back(Copyable{}); 
} 

mit diesem nicht kompilieren auf MSVC

In beiden Fällen, wenn ich das explizit gelöschte Move Construct/Assign entfernen Methoden kompiliert der Code. AFAIK, wenn Sie Methoden zum Zuweisen/Konstruieren von Kopien deklarieren, deklariert der Compiler die entsprechenden Verschiebeelemente nicht implizit. Also sollten sie trotzdem effektiv gelöscht werden, oder? Warum kompiliert der Code, wenn ich das explizite Löschen von move construct/assign entferne?

Was ist eine gute Problemumgehung für diesen C++ 11 Defekt im Allgemeinen? Ich möchte nicht, dass meine Objekte beweglich sind (aber sie sind kopierbar).

+0

Implementierung über eine Kopie implementieren? –

+0

Auch bezogen auf: [Warum partizipieren C++ 11-gelöschte Funktionen an der Überladungsauflösung?] (Http: // stackoverflow.com/questions/14085620/why-do-c11-deleted-functions-partition-in-overload-resolution) – Nawaz

Antwort

14

Das Löschen einer Funktion ist nicht dasselbe wie das Deklarieren.

Eine gelöschte Funktion wird deklariert und nimmt an der Überladungsauflösung teil, aber wenn Sie versuchen, sie aufzurufen, wird ein Fehler erzeugt.

Wenn Sie Ihren Move-Konstruktor nicht deklarieren, erstellt der Compiler beim Erstellen eines Kopierkonstruktors keinen solchen Konstruktor. Die Überladungsauflösung für einen R-Wert wird Ihren Kopierkonstruktor finden, was Sie wahrscheinlich wollen.

Was Sie mit Ihrer foo(foo&&)=delete gesagt haben, war "wenn jemand versucht, dieses Objekt zu konstruieren, erzeugen Sie einen Fehler".

kann ich den Unterschied verdeutlichen:

void do_stuff(int x) { std::cout << x << "\n"; } 
void do_stuff(double) = delete; 

void do_stuff2(int x) { std::cout << x << "\n"; } 
//void do_stuff2(double) = delete; 

int main() { 
    do_stuff(3); // works 
    //do_stuff(3.14); // fails to compile 
    do_stuff2(3); // works 
    do_stuff2(3.14); // works, calls do_stuff2(int) 
} 

der einzigen Teil mit Ihrem obigen Problem, dass dies ein bisschen mehr verwirrend macht, ist, dass spezielle Elementfunktionen automatisch erstellt werden oder nicht leicht obskure Regeln basiert.

10
Copyable(Copyable&&) = delete; 
Copyable& operator= (Copyable&&) = delete; 

Sofern Sie ein Experte in Bewegung Semantik sind (und ich meine, wirklich kenntnisreich), nie die besondere bewegen Mitglieder löschen. Es wird nicht tun, was Sie wollen. Wenn Sie den Code von jemand anderem überprüfen, der dies getan hat, rufen Sie ihn an. Die Erklärung muss wirklich solide sein und nicht "weil ich nicht möchte, dass sich der Typ bewegt".

Tun Sie es einfach nicht.

Der richtige Weg zu tun, was Sie wollen, ist einfach zu erklären/definieren Sie Ihre Kopie Mitglieder. Die Move-Mitglieder werden implizit gesperrt (nicht gelöscht, aber nicht vorhanden). Schreiben Sie einfach C++ 98/03.

Für weitere Details, see this answer.

+1

+1 das ist die praktische beratung, die schwer zu finden ist, obwohl es viele hilfreiche hinweise gibt. –

+2

Wie wäre es, ein kleines Buch mit all diesen großartigen Dingen zu schreiben (Bewegungssemantik, Parameterübergabe, Klassendesign, Daten, Permutationen und Threads), die Sie hier im Laufe der Jahre auf SO (und in libC++ verwendet) erstellt haben? Wie @ShafikYaghmour richtig sagt, ist es sehr schwer zu finden und verstreut. – TemplateRex

+0

@TemplateRex: Auf der Suche nach der Zeit ... –