91

私は単純なプログラマーです。私のクラスメンバー変数は、ほとんどの場合、PODタイプとSTLコンテナーで構成されています。このため、代入演算子やコピーコンストラクターを作成する必要はほとんどありません。これらはデフォルトで実装されているためです。

これに加えて、std::move移動できないオブジェクトで使用する場合は、代入演算子を使用します。つまりstd::move、完全に安全です。

this->member1_ = std::move(other.member1_);...私は単純なプログラマーなので、コンパイラーが単純に「 」として実装できるため、作成するすべてのクラスに移動コンストラクター/代入演算子を追加せずに、移動機能を利用したいと思います。

しかし、そうではありません(少なくともVisual 2010ではそうではありません)。これには特別な理由がありますか?

さらに重要なことには; これを回避する方法はありますか?

更新: GManNickGの答えを見下ろすと、彼はこのための優れたマクロを提供します。また、知らなかった場合は、move-semanticsを実装すると、スワップメンバー関数を削除できます。

4

4 に答える 4

77

ムーブ コンストラクターと代入演算子の暗黙的な生成は論争の的となっており、C++ 標準の最近のドラフトでは大幅な改訂が行われているため、現在利用可能なコンパイラは暗黙的な生成に関して異なる動作をする可能性があります。

この問題の歴史の詳細については、2010 WG21 の論文リストを参照し、 「mov」を検索してください。

現在の仕様 (N3225、11 月から) には次のように記載されています (N3225 12.8/8)。

クラスの定義でXムーブ コンストラクターが明示的に宣言されていない場合、1 つが暗黙的にデフォルトとして宣言されます。

  • Xユーザー宣言のコピー コンストラクターがなく、

  • Xユーザー宣言のコピー代入演算子がありません。

  • Xユーザー宣言の移動代入演算子がない

  • Xユーザー宣言のデストラクタがなく、かつ

  • 移動コンストラクターは、暗黙的に削除済みとして定義されません。

12.8/22 には、移動代入演算子が暗黙的にデフォルトとして宣言されるタイミングを指定する同様の言語があります。暗黙的な移動生成の現在の仕様をサポートするために行われた変更の完全なリストは、N3203: 暗黙的な移動を生成するための条件を引き締めます 。これは主に、Bjarne Stroustrup の論文N3201: Moving right alongで提案された解決策の 1 つに基づいていました。

于 2011-01-27T18:33:15.683 に答える
13

暗黙的に生成されたムーブ コンストラクターは標準で考慮されていますが、危険な場合があります。Dave Abrahams の分析を参照してください。

ただし、最終的には、標準にはムーブ コンストラクターとムーブ代入演算子の暗黙的な生成が含まれていましたが、かなりの制限リストがありました。

クラス X の定義でムーブ コンストラクターが明示的に宣言されて
いない場合、X にユーザー宣言のコピー コンストラクターがない場合、および X
にユーザー宣言のコピー代入演算子がない場合にのみ、暗黙的にデフォルトとして宣言されます。 ,
— X には、ユーザー宣言の移動代入演算子
がありません。 — X には、ユーザー宣言のデストラクタがありません
。 — 移動コンストラクタは、暗黙的に削除済みとして定義されません。

話の内容はそれだけではありません。ctor は宣言できますが、削除済みとして定義されたままです。

暗黙的に宣言されたコピー/移動コンストラクターは、そのクラスのインライン パブリック メンバーです。クラス X のデフォルトのコピー/移動コンストラクターは、X が次の場合に削除済み (8.4.3) として定義されます。

— 非自明な対応するコンストラクターを持ち、X が共用体のようなクラスであるバリアント メンバー —
オーバーロードの解決 (13.3) のためにコピー/移動できないクラス型 M (またはその配列) の非静的データ メンバー。 M の対応するコンストラクターに適用されると、あいまいさ、またはデフォルトのコンストラクターから削除またはアクセスできない関数が発生します
— B の対応するコンストラクターに適用される、オーバーロード解決 (13.3) のためにコピー/移動できない直接または仮想基底クラス B 、削除された、またはデフォルトのコンストラクターからアクセスできないあいまいさまたは関数になります。
— デフォルトのコンストラクターから削除またはアクセスできないデストラクタを持つ型の直接または仮想基本クラスまたは非静的データ メンバー。
— コピー コンストラクターの場合、右辺値参照型の非静的データ メンバー、または
— 移動コンストラクターの場合、非静的データ メンバー、または移動コンストラクターを持たず自明ではない型を持つ直接または仮想基底クラスコピー可能。

于 2011-01-27T17:54:40.413 に答える
8

(今のところ、私は愚かなマクロに取り組んでいます...)

ええ、私もその道を行きました。マクロは次のとおりです。

// detail/move_default.hpp
#ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP
#define UTILITY_DETAIL_MOVE_DEFAULT_HPP

#include <boost/preprocessor.hpp>

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther));

#define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember))
#define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember);

#define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        ,                                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)                                                   \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases))                      \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases)  \
                                                                                                        \
            return *this;                                                                               \
        }

#define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)                                               \
        pT(pT&& pOther) :                                                                               \
        BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(                                                       \
            UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers))                         \
        {}                                                                                              \
                                                                                                        \
        pT& operator=(pT&& pOther)                                                                      \
        {                                                                                               \
            BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers)     \
                                                                                                        \
            return *this;                                                                               \
        }

#endif

</p>

// move_default.hpp
#ifndef UTILITY_MOVE_DEFAULT_HPP
#define UTILITY_MOVE_DEFAULT_HPP

#include "utility/detail/move_default.hpp"

// move bases and members
#define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers)

// base only version
#define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases)

// member only version
#define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers)

#endif

(長さとドキュメンタリーである本当のコメントを削除しました。)

クラスのベースやメンバーをプリプロセッサ リストとして指定します。次に例を示します。

#include "move_default.hpp"

struct foo
{
    UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str));

    int x;
    std::string str;
};

struct bar : foo, baz
{
    UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz));
};

struct baz : bar
{
    UTILITY_MOVE_DEFAULT(baz, (bar), (ptr));

    void* ptr;
};

そして、move-constructor と move-assignment operator が出てきます。

(余談ですが、詳細を 1 つのマクロに結合する方法を誰かが知っていれば、それはうねりです。)

于 2011-01-28T08:39:32.670 に答える
5

VS2010 は、実装時に標準ではなかったため、それを行いません。

于 2011-01-27T18:25:40.490 に答える