質問

新しい移動セマンティクスをテストします。

Move Constructorで抱えていた問題について尋ねました。しかし、コメントで判明したように、問題は、標準の「コピーアンドスワップ」イディオムを使用したときに「移動割り当て」オペレーターと「標準割り当て」オペレーターが衝突することです。

これは私が使用しているクラスです。

#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;
        }
};

かなり沼地の標準だと思います。

次に、このようなコードをテストしました。

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

ここに割り当て a 「移動割り当て」オペレーターを使用する必要があります。しかし、「標準の割り当て」オペレーター(標準のコピーとスワップとして書かれている)との衝突があります。

> 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)
                ^

これで、「標準割り当て」オペレーターを変更することでこれを修正できます。

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

しかし、これは、コピーとスワップを最適化するコンパイラの能力を台無しにするため、良くありません。コピーアンドスワップイディオムは何ですか? ここここ

それほど明白ではないものが足りませんか?

役に立ちましたか?

解決

割り当てオペレーターを値を取得するように定義する場合、rvalue-referenceを取得する割り当てオペレーターを定義してはいけません(必要ではありません)。それには意味がありません。

一般に、lvalueをrvalueと区別する必要がある場合に、rvalue-referenceを取得する過負荷を提供する必要がありますが、この場合、実装の選択は、その区別を行う必要がないことを意味します。 LValueを持っているか、RValueがあるかにかかわらず、引数を作成して内容を交換します。

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

この場合、コンパイラは電話を解決します a.operator=(f()); 返品値の唯一の理由は、 operator= コピーを削除します - これは、最初に関数を値にするポイントです!

他のヒント

他の答えは、過負荷が1つしかないことを示唆しています operator =(String rhs) 価値で議論をするが、これはそうです いいえ 最も効率的な実装。

この例では、DavidRodríguez -Dribeasによる事実です

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

コピーは作成されていません。ただし、ただ仮定します operator =(String rhs) 提供され、この例を考慮してください。

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

何が起こるかです

  1. b にコピーされます rhs (メモリ割り当て + memcpy);
  2. arhs 交換されます。
  3. rhs 破壊されます。

実装する場合 operator =(const String& rhs)operator =(String&& rhs) 次に、ターゲットの長さがソースよりも大きい場合のステップ1のメモリ割り当てを回避できます。たとえば、これは単純な実装です(完璧ではありません:より良い場合があります String 持っていた capacity メンバー):

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);
}

パフォーマンスポイントに加えて swapnoexcept, 、 それから operator =(String&&) 可能です noexcept 同じように。 (メモリの割り当てが「潜在的に」実行されている場合はそうではありません。)

この優れた詳細をご覧ください 説明 ハワード・ヒナントによる。

コピーと割り当てに必要なのはこれだけです:

    // 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
   }
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top