Frage

Testen der neuen Bewegungssemantik.

Ich habe nur nach Problemen gefragt, die ich mit dem Move Constructor hatte. Wie sich jedoch in den Kommentaren herausstellt, ist das Problem wirklich, dass der Operator "Verschiebener Zuweisungen" und "Standardzuweisung" -Operator bei der Verwendung der IDIOM "Kopier- und Swap" -Standeskopie auftreten.

Dies ist die Klasse, die ich benutze:

#include <string.h>
#include <utility>

class String
{
    int         len;
    char*       data;

    public:
        // Default constructor
        // In Terms of C-String constructor
        String()
            : String("")
        {}

        // Normal constructor that takes a C-String
        String(char const* cString)
            : len(strlen(cString))
            , data(new char[len+1]()) // Allocate and zero memory
        {
            memcpy(data, cString, len);
        }

        // Standard Rule of three
        String(String const& cpy)
            : len(cpy.len)
            , data(new char[len+1]())
        {
            memcpy(data, cpy.data, len);
        }
        String& operator=(String rhs)
        {
            rhs.swap(*this);
            return *this;
        }
        ~String()
        {
            delete [] data;
        }
        // Standard Swap to facilitate rule of three
        void swap(String& other) throw ()
        {
            std::swap(len,  other.len);
            std::swap(data, other.data);
        }

        // New Stuff
        // Move Operators
        String(String&& rhs) throw()
            : len(0)
            , data(null)
        {
            rhs.swap(*this);
        }
        String& operator=(String&& rhs) throw()
        {
            rhs.swap(*this);
            return *this;
        }
};

Hübscher Moorstandard, denke ich.

Dann habe ich meinen Code so getestet:

int main()
{
    String  a("Hi");
    a   = String("Test Move Assignment");
}

Hier die Aufgabe zu a Sollte den Operator "Verschiebener Zuweisung" verwenden. Es gibt jedoch einen Konflikt mit dem "Standardzuweisungsbetreiber" (der als Standardkopie und Swap geschrieben ist).

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

> g++ -std=c++11 String.cpp
String.cpp:64:9: error: use of overloaded operator '=' is ambiguous (with operand types 'String' and 'String')
    a   = String("Test Move Assignment");
    ~   ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String.cpp:32:17: note: candidate function
        String& operator=(String rhs)
                ^
String.cpp:54:17: note: candidate function
        String& operator=(String&& rhs)
                ^

Jetzt kann ich dies beheben, indem ich den Operator "Standardzuweisungen" an:

    String& operator=(String const& rhs)
    {
        String copy(rhs);
        copy.swap(*this);
        return *this;
    }

Dies ist jedoch nicht gut, da es mit der Fähigkeit des Compilers, die Kopie und den Austausch zu optimieren, durcheinander bringt. Sehen Sie, was ist die Kopie-und-Swap-Idiom? hier und hier

Vermisse ich etwas nicht so offensichtlich?

War es hilfreich?

Lösung

Wenn Sie den Zuordnungsbetreiber so definieren, dass Sie einen Wert annehmen, sollten Sie den Zuordnungsbetreiber nicht definieren (nicht und nicht), wenn Sie eine RValue-Referenz einnehmen. Es macht keinen Sinn.

Im Allgemeinen müssen Sie nur eine Überlastung anbieten, wenn Sie eine RValue-Referenz nutzen, wenn Sie einen LVALUE von einem RValue unterscheiden müssen. In diesem Fall bedeutet Ihre Implementierungswahl jedoch, dass Sie diese Unterscheidung nicht treffen müssen. Egal, ob Sie einen LVALUE oder ein RValue haben, Sie werden das Argument erstellen und den Inhalt austauschen.

String f();
String a;
a = f();   // with String& operator=(String)

In diesem Fall wird der Compiler den Anruf auflösen, um zu sein a.operator=(f()); Es wird erkennen, dass der einzige Grund für den Rückgabewert das Argument ist operator= und wird jede Kopie elide -dies ist der Sinn, die Funktion überhaupt einen Wert zu machen!

Andere Tipps

Andere Antworten deuten darauf hin, dass nur eine Überladung verfügt operator =(String rhs) das Argument nach Wert nehmen, aber das ist nicht die effizienteste Implementierung.

Es ist wahr, dass in diesem Beispiel von David Rodríguez - Dribeas

String f();
String a;
a = f();   // with String& operator=(String)

Es wird keine Kopie gemacht. Nehmen Sie jedoch nur an operator =(String rhs) wird bereitgestellt und berücksichtigt dieses Beispiel:

String a("Hello"), b("World");
a = b;

Was passiert ist

  1. b wird kopiert zu rhs (Speicherzuweisung + memcpy);
  2. a und rhs werden ausgetauscht;
  3. rhs ist zerstört.

Wenn wir implementieren operator =(const String& rhs) und operator =(String&& rhs) Dann können wir die Speicherzuweisung in Schritt 1 vermeiden, wenn das Ziel eine größere Länge hat als die von Quelle. Zum Beispiel ist dies eine einfache Implementierung (nicht perfekt: könnte besser sein, wenn String hatte a capacity Mitglied):

String& operator=(const String& rhs) {
    if (len < rhs.len) {
        String tmp(rhs);
        swap(tmp);
    else {
        len = rhs.len;
        memcpy(data, rhs.data, len);
        data[len] = 0;
    }
    return *this;
}

String& operator =(String&& rhs) {
    swap(rhs);
}

Zusätzlich zum Leistungspunkt, wenn swap ist noexcept, dann operator =(String&&) kann sein noexcept auch. (Dies ist nicht der Fall, wenn die Speicherzuweisung "möglicherweise" durchgeführt wird.)

Weitere Informationen finden Sie in diesem exzellenten Erläuterung von Howard Hinnant.

Alles, was Sie für Kopie und Zuordnung benötigen, ist Folgendes:

    // As before
    String(const String& rhs);

    String(String&& rhs)
    :   len(0), data(0)
    {
        rhs.swap(*this);
    }

    String& operator = (String rhs)
    {
        rhs.swap(*this);
        return *this;
    }

   void swap(String& other) noexcept {
       // As before
   }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top