web-dev-qa-db-ja.com

基本クラスがそうでないのに、派生クラスの移動がなぜ構築可能であるのですか?

次の例について考えてみます。

#include <iostream>
#include <string>
#include <utility>

template <typename Base> struct Foo : public Base {
    using Base::Base;
};

struct Bar {
    Bar(const Bar&) { }
    Bar(Bar&&) = delete;
};

int main() {
    std::cout << std::is_move_constructible<Bar>::value << std::endl; // NO
    std::cout << std::is_move_constructible<Foo<Bar>>::value << std::endl; // YES. Why?!
}

基本クラスがmove-constructableでないにもかかわらず、コンパイラーがmoveコンストラクターを生成するのはなぜですか?

それは標準にありますか、それともコンパイラのバグですか?ベースから派生クラスに移動構成を「完全に伝播」することは可能ですか?

57
Dmitry

なぜなら:

削除済みとして定義されているデフォルトの移動コンストラクターは、オーバーロードの解決によって無視されます。

([class.copy]/11)

Barの移動コンストラクターは明示的に削除であるため、Barは移動できません。ただし、Barメンバーは移動できないため、Foo<Bar>の移動コンストラクターは、デフォルトとして暗黙的に宣言された後、implicitly deletedになります。したがって、Foo<Bar>は、そのコピーコンストラクターを使用して移動できます。

編集:using Base::Baseなどの継承コンストラクター宣言がデフォルト、コピー、または移動コンストラクターを継承しないという重要な事実も忘れていました。そのため、Foo<Bar>には明示的に削除された移動コンストラクターがありません。 Barから継承。

30
Brian

1。 std::is_move_constructibleの動作

これは std :: is_move_constructible の予想される動作です:

移動コンストラクタはないが、const T&引数を受け入れるコピーコンストラクタがある型は、std::is_move_constructibleを満たします。

つまり、コピーコンストラクターを使用しても、右辺値参照T&&からTを構築することが引き続き可能です。また、Foo<Bar>には 暗黙的に宣言されたコピーコンストラクタ があります。

2。 Foo<Bar>の暗黙的に宣言された移動コンストラクタ

ベースクラスがmove-constructableでないにもかかわらず、コンパイラーがmoveコンストラクターを生成するのはなぜですか?

実際、Foo<Bar>の移動コンストラクターは deleted として定義されていますが、削除された暗黙的に宣言された移動コンストラクターは、オーバーロードの解決によって無視されることに注意してください。

クラスTの暗黙的に宣言された、またはデフォルトの移動コンストラクターは、次のいずれかに該当する場合に削除済みとして定義されています。

...
T has direct or virtual base class that cannot be moved (has deleted, inaccessible, or ambiguous move constructors); 
...

削除された暗黙的に宣言された移動コンストラクターは、オーバーロードの解決によって無視されます(それ以外の場合、右辺値からのコピーの初期化が妨げられます).

3。 BarFoo<Bar>の異なる動作

Barの移動コンストラクターは明示的にdeletedとして宣言され、Foo<Bar>の移動コンストラクターは暗黙的に宣言されてdeletedとして定義されることに注意してください。重要なのは、削除された暗黙的に宣言された移動コンストラクターがオーバーロード解決によって無視されることです。これにより、コピーで構造体Foo<Bar>を移動できます。コンストラクタ。ただし、明示的に削除された移動コンストラクターはオーバーロードの解決に参加します。つまり、コンストラクターBarを移動しようとすると、削除された移動コンストラクターが選択され、プログラムの形式が正しくありません。

そのため、Foo<Bar>は移動構成可能ですが、Barは構成可能ではありません。

この規格には、これに関する明確な記述があります。 $ 12.8/11クラスオブジェクトのコピーと移動[class.copy]

削除済みとして定義されているデフォルトの移動コンストラクターは、オーバーロード解決([over.match]、[over.over])によって無視されます。 [注:削除された移動コンストラクターは、コピーコンストラクターを代わりに使用できる右辺値からの初期化に干渉します。 —エンドノート]

24
songyuanyao