19

編集:解決済みコメントを参照-回答なしで解決済みとしてマークする方法がわからない。

c ++ 0xでのパーフェクトフォワーディング/移動セマンティクスに関するチャンネル9のビデオを見た後、私はこれが新しい代入演算子を書くための良い方法であると信じるようになりました。

#include <string>
#include <vector>
#include <iostream>

struct my_type 
{
    my_type(std::string name_)
            :    name(name_)
            {}

    my_type(const my_type&)=default;

    my_type(my_type&& other)
    {
            this->swap(other);
    }

    my_type &operator=(my_type other)
    {
            swap(other);
            return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
    void operator=(const my_type&)=delete;  
    void operator=(my_type&&)=delete;
};


int main()
{
    my_type t("hello world");
    my_type t1("foo bar");
    t=t1;
    t=std::move(t1);
}

これにより、r値と定数の両方を割り当てることができます。適切なコンストラクターを使用して新しいオブジェクトを作成し、その内容を*thisと交換します。必要以上にデータがコピーされないので、これは私には聞こえます。そして、ポインタ演算は安価です。

しかし、私のコンパイラは同意しません。(g ++ 4.6)そして私はこれらのエラーを受け取ります。

copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note:   no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>

私は何か間違ったことをしていますか?これは悪い習慣ですか(自分で割り当てているかどうかをテストする方法はないと思います)?コンパイラはまだ準備ができていませんか?

ありがとう

4

3 に答える 3

24

コピー/スワップ割り当てのイディオムには非常に注意してください。特に注意深い分析なしで適用された場合、それは最適ではない可能性があります。代入演算子に強力な例外安全性が必要な場合でも、その機能を他の方法で取得できます。

あなたの例のために私はお勧めします:

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

これにより、std::stringのコピーおよび移動メンバーに転送される暗黙のコピーおよび移動セマンティクスが取得されます。そして、std :: stringの作成者は、これらの操作を実行する方法を最もよく知っています。

コンパイラが暗黙的な移動生成をまだサポートしていないが、デフォルトの特殊メンバーをサポートしている場合は、代わりにこれを行うことができます。

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype&) = default;
    my_type& operator=(const mytype&) = default;
    my_type(mytype&&) = default;
    my_type& operator=(mytype&&) = default;

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

特別なメンバーについて明確にしたい場合は、上記を選択することもできます。

デフォルトの特殊メンバー(または暗黙の移動メンバー)をまだサポートしていないコンパイラーを扱っている場合は、完全にC ++ 11に準拠したときに、コンパイラーが最終的にデフォルトにするものを明示的に指定できます。

struct my_type 
{
    my_type(std::string name_)
            :    name(std::move(name_))
            {}

    my_type(const mytype& other)
        : name(other.name) {}
    my_type& operator=(const mytype& other)
    {
        name = other.name;
        return *this;
    }
    my_type(mytype&& other)
        : name(std::move(other.name)) {}
    my_type& operator=(mytype&& other)
    {
        name = std::move(other.name);
        return *this;
    }

    void swap(my_type &other)
    {
            name.swap(other.name);
    }

private:
    std::string name;
};

割り当てに強力な例外安全性が本当に必要な場合は、一度設計して明示的にしてください(Luc Dantonによる提案を含めるように編集してください)。

template <class C>
typename std::enable_if
<
    std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    c = std::move(other);
    return c;
}

template <class C>
typename std::enable_if
<
    !std::is_nothrow_move_assignable<C>::value,
    C&
>::type
strong_assign(C& c, C other)
{
    using std::swap;
    static_assert(std::is_nothrow_swappable_v<C>,  // C++17 only
                  "Not safe if you move other into this function");
    swap(c, other);
    return c;
}

これで、クライアントは効率(my type :: operator =)を選択するか、を使用して強力な例外安全性を選択できますstrong_assign

于 2011-09-17T22:20:14.880 に答える
2

エラーメッセージをよく読みましたか?複数のコピー代入演算子と複数のムーブ代入演算子があるという2つのエラーが表示されます。そしてそれは正確に正しいです!

特別なメンバーは、デフォルト、削除、従来の定義、または除外によって暗黙的に処理されるかどうかに関係なく、最大で1回指定する必要があります。2つのコピー代入演算子(1つは取るmy_type、もう1つは取るmy_type const &)と2つのムーブ代入演算子(1つは取るmy_type、もう1つは取るmy_type &&)があります。取る代入演算子はmy_type左辺値と右辺値の参照を処理できるため、コピー代入とムーブ代入の両方として機能することに注意してください。

ほとんどの特別なメンバーの関数シグネチャには複数の形式があります。1つ選ぶ必要があります。異常なものを使用してから従来のものを削除することはできません。これは二重宣言になるためです。コンパイラーは、異常な形式の特殊メンバーを自動的に使用し、従来の署名を使用して特殊メンバーを合成しません。

(エラーには3つの候補が記載されていることに注意してください。割り当てタイプごとに、適切な削除されたメソッド、を実行するメソッドmy_type、および緊急のニアマッチとして他の削除されたメソッドが表示されます。)

于 2011-10-27T10:53:37.053 に答える
0

代入演算子のこれらのオーバーロードを削除することになっていますか?代入演算子の宣言はテンプレートか何かであるべきではありませんか?それがどのように機能するのか、私にはよくわかりません。

それがうまくいったとしても、そのようにムーブ代入演算子を実装することで、移動したばかりのオブジェクトが保持していたリソースは、割り当ての時点ではなく、その存続期間が終了したときに解放されることに注意してください。詳細については、こちらをご覧ください。

http://cpp-next.com/archive/2009/09/your-next-assignment/

于 2011-09-17T22:06:11.700 に答える