次の例について考えてみます。
#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コンストラクターを生成するのはなぜですか?
それは標準にありますか、それともコンパイラのバグですか?ベースから派生クラスに移動構成を「完全に伝播」することは可能ですか?
なぜなら:
削除済みとして定義されているデフォルトの移動コンストラクターは、オーバーロードの解決によって無視されます。
([class.copy]/11)
Bar
の移動コンストラクターは明示的に削除であるため、Bar
は移動できません。ただし、Bar
メンバーは移動できないため、Foo<Bar>
の移動コンストラクターは、デフォルトとして暗黙的に宣言された後、implicitly deletedになります。したがって、Foo<Bar>
は、そのコピーコンストラクターを使用して移動できます。
編集:using Base::Base
などの継承コンストラクター宣言がデフォルト、コピー、または移動コンストラクターを継承しないという重要な事実も忘れていました。そのため、Foo<Bar>
には明示的に削除された移動コンストラクターがありません。 Bar
から継承。
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。 Bar
とFoo<Bar>
の異なる動作
Bar
の移動コンストラクターは明示的にdeleted
として宣言され、Foo<Bar>
の移動コンストラクターは暗黙的に宣言されてdeleted
として定義されることに注意してください。重要なのは、削除された暗黙的に宣言された移動コンストラクターがオーバーロード解決によって無視されることです。これにより、コピーで構造体Foo<Bar>
を移動できます。コンストラクタ。ただし、明示的に削除された移動コンストラクターはオーバーロードの解決に参加します。つまり、コンストラクターBar
を移動しようとすると、削除された移動コンストラクターが選択され、プログラムの形式が正しくありません。
そのため、Foo<Bar>
は移動構成可能ですが、Bar
は構成可能ではありません。
この規格には、これに関する明確な記述があります。 $ 12.8/11クラスオブジェクトのコピーと移動[class.copy]
削除済みとして定義されているデフォルトの移動コンストラクターは、オーバーロード解決([over.match]、[over.over])によって無視されます。 [注:削除された移動コンストラクターは、コピーコンストラクターを代わりに使用できる右辺値からの初期化に干渉します。 —エンドノート]