web-dev-qa-db-ja.com

ユーザー定義のmove-constructorが暗黙のcopy-constructorを無効にするのはなぜですか?

Boost/shared_ptr.hppを読んでいるときに、次のコードを見た。

//  generated copy constructor, destructor are fine...

#if defined( BOOST_HAS_RVALUE_REFS )

// ... except in C++0x, move disables the implicit copy

shared_ptr( shared_ptr const & r ): px( r.px ), pn( r.pn ) // never throws
{
}

#endif

「生成されたコピーコンストラクター、デストラクタはC++ 11を除いて問題ありません。移動すると暗黙的なコピーが無効になります」というコメントはどういう意味ですか? C++ 11でこの状況を防ぐために、常にコピートラクターを自分で作成しませんか?

40
amazingjxq

Ildjarnの回答は、正確でユーモラスなものだと思ったので賛成しました。 :-)

OPが知りたいかもしれない質問のタイトルが原因であると想定しているため、私は代替の回答を提供していますなぜ標準はそう言っています。

背景

so Cと互換性がないため、C++は暗黙的にコピーメンバーを生成しました。そうでない場合は、1985年に生まれました。この場合、この会話はありません。今日はC++が存在しなかったためです。

とはいえ、暗黙的に生成されたコピーメンバーは、「悪魔との取引」に似ています。 C++は、それらなしには生まれませんでした。しかし、それらはかなりの数のインスタンスで誤って不正なコードを生成するという点で悪です。 C++委員会は愚かではありません、彼らはこれを知っています。

C++ 11

C++が誕生し、成長した大人へと成長した今、委員会はただ言いたいだけです。暗黙的に生成されたコピーメンバーはもうありません。彼らはあまりにも危険です。暗黙的に生成されたコピーメンバーが必要な場合は、その決定にオプトインする必要があります(オプトアウトではなく)。ただし、これが行われた場合に破損する既存のC++コードの量を考慮すると、自殺することになります。かなり正当化されるhugeの後方互換性の問題があります。

したがって、委員会は妥協的な立場に達しました:moveメンバーを宣言する場合(これはレガシーC++コードでは実行できません)、デフォルトのコピーメンバーが間違ったことを行う可能性が高いと想定します。必要に応じてオプトイン(=defaultを使用)。または自分で書いてください。それ以外の場合は、暗黙的に削除されます。移動のみのタイプがある世界でのこれまでの経験は、このデフォルトの位置が実際には非常に一般的に望ましいものであることを示しています(例:unique_ptrofstreamfutureなど)。 。そして、オプトインの費用は実際に= defaultを使用すると非常に小さくなります。

今後の展望

委員会は、「デストラクタを作成した場合、暗黙のコピーメンバーが正しくない可能性があるため、削除します。これはC++ 98/03の「3つのルール」です。ただし、それでも多くのコードが壊れます。ただし、委員会はC++ 11で、ユーザーが宣言したデストラクタを提供すると、コピーメンバーの暗黙的な生成非推奨になると述べています。つまり、この機能は将来の標準で削除される可能性があります。そして、いつの日か、コンパイラーはこの状況で「非推奨の警告」の発行を開始する可能性があります(標準では警告を指定できません)。

結論

ですから、C++は数十年にわたって成長し、成熟してきました。そして、それはあなたの父親のC++があなたの子供のC++に対処するために移行する必要があるかもしれないことを意味します。これはゆっくりとした段階的なプロセスであるため、手を放したり、別の言語に移植したりする必要はありません。しかし、それは遅いとしてもis変化します。

102
Howard Hinnant

C++標準がそう言っているから–§12.8/ 7:

クラス定義でコピーコンストラクターが明示的に宣言されていない場合は、コピーコンストラクターが宣言されます暗黙的にクラス定義が移動コンストラクターまたは移動割り当て演算子を宣言する場合、暗黙的に宣言されたコピーコンストラクターは削除済みとして定義されます;それ以外の場合は、デフォルトとして定義されます。後者のケースは、クラスにユーザー宣言のコピー代入演算子またはユーザー宣言のデストラクタがある場合は推奨されません。したがって、クラス定義の

struct X {
    X(const X&, int);
};

コピーコンストラクタは暗黙的に宣言されています。ユーザーが宣言したコンストラクターが後で次のように定義されている場合

X::X(const X& x, int i =0) { /* ... */ }

xのコピーコンストラクタを使用すると、あいまいさが原因で不正な形式になります。診断は必要ありません。

(エンファシス鉱山。)

24
ildjarn