2

そのため、Move Semantics をいじってみましょう。

したがって、これを最初に見たのは次のようになりました。

 class String
 {
     char*   data;
     int     len;
     public:
         // Normal rule of three applied up here.
         void swap(String& rhs) throw()
         {
            std::swap(data, rhs.data);
            std::swap(len,  rhs.len);
         }
         String& operator=(String rhs) // Standard Copy and swap. 
         {
            rhs.swap(*this);
            return *this;
         }

         // New Stuff here.
         // Move constructor
         String(String&& cpy) throw()    // ignore old throw construct for now.  
            : data(NULL)
            , len(0)
         {
            cpy.swap(*this);
         }
         String& operator=(String&& rhs) throw() 
         {
            rhs.swap(*this);
            return *this;
         }
};

これを見て。Move 代入に関して Move コンストラクターを定義する価値があるかもしれません。対称性が高く、DRY に見える (そしてコピー アンド スワップのように見える) ので気に入っています。

そこで、Move Constructor を次のように書き直しました。

         String(String&& cpy) throw() 
            : data(NULL)
            , len(0)
         {
            operator=(std::move(cpy));
         }

しかし、これはあいまいなエラーを生成します:

String.cpp:45:9: error: call to member function 'operator=' is ambiguous
        operator=(std::move(rhs));
        ^~~~~~~~~
String.cpp:32:13: note: candidate function
    String& operator=(String rhs)
            ^
String.cpp:49:13: note: candidate function
    String& operator=(String&& rhs) throw()
            ^
1 error generated.

std::move()引数を渡すときに使用したので、これが Move 代入演算子にバインドされることを期待していました。私は何を間違っていますか?

4

4 に答える 4

0

(3番目の回答を追加して申し訳ありませんが、満足のいく解決策がようやく得られたと思います。ideoneのデモ

これらのメソッドの両方を持つクラスがあります。

String& operator=(String copy_and_swap);
String& operator=(String && move_assignment);

問題はあいまいさです。2 番目のオーバーロードが実行可能な場合は、より効率的になる可能性があるため、2 番目のオプションを支持するタイブレーカーが必要です。したがって、最初のバージョンをテンプレート化されたメソッドに置き換えます。

template<typename T>
String& operator=(T templated_copy_and_swap);
String& operator=(String && move_assignment);

このタイブレークは後者を優先しますが、残念ながら次のエラー メッセージが表示されます。

しかし、これは修正できます。コピー代入演算子を宣言して、暗黙的に削除することを決定しないようにする必要がありますが、これ以上あいまいさを導入しないようにする必要もあります。これを行う1つの方法を次に示します。

const volatile String&& operator=(String&) volatile const && = delete;

deletedこれで、適切なタイブレークを持つ 3 つの代入演算子 (そのうちの 1 つは) ができました。に注意してくださいvolatile const &&。これの目的は、このオーバーロードの優先順位を非常に低くするために、できるだけ多くの修飾子を単純に追加することです。そして、万一、 であるオブジェクトに代入しようとするとvolatile const &&、コンパイラ エラーが発生し、それを処理することができます。

(clang 3.3 および g++-4.6.3 でテストし、必要な数のコピーとスワップを実行します (つまり、可能な限り少なくします! g++ では、volatile const代わりに必要ですvolatile const &&が、それで問題ありません。)

編集: 型推定のリスク: templated の実装では、 のoperator=ような推定型に注意することを検討することをお勧めしますstatic_assert( std::is_same<T,String>(), "This should only accept Strings. Maybe SFINAE and enable_if on the return value?");

于 2013-11-07T23:49:18.773 に答える