Stack Overflow の投稿Checking the object type in C++11に、次のコメントがあります。
C++11 では、実際にやりたいことがあります
virtual ~A() = default;
。そうしないと、暗黙の移動コンストラクターが失われます。
何virtual ~A() = default;
のためですか?暗黙の移動コンストラクターが で失われるのはvirtual ~A() {}
なぜですか?
Stack Overflow の投稿Checking the object type in C++11に、次のコメントがあります。
C++11 では、実際にやりたいことがあります
virtual ~A() = default;
。そうしないと、暗黙の移動コンストラクターが失われます。
何virtual ~A() = default;
のためですか?暗黙の移動コンストラクターが で失われるのはvirtual ~A() {}
なぜですか?
コメントが正しくありません。
両方:
virtual ~A() = default;
と
virtual ~A() {}
ユーザーが宣言しています。また、デストラクタがユーザーによって宣言されている場合、暗黙的な移動メンバーは禁止されます。
[dcl.fct.def.default]/p4 では、ユーザー宣言およびユーザー提供の特殊メンバーについて説明しています。
ユーザーが宣言し、最初の宣言で明示的にデフォルト設定または削除されていない場合、特別なメンバー関数はユーザー提供です。
この投稿https://stackoverflow.com/a/17204598/260127には、次のコメントがあります。
C++11 では、実際にやりたいことがあります
virtual ~A() = default;
。そうしないと、暗黙の移動コンストラクターが失われます。
コメントが正しくありません。
default
edでさえ、そのデストラクタは「ユーザー宣言」です(ただし、「ユーザー提供」でもないことに注意してください)。
#include <iostream>
struct Helper
{
Helper() {}
Helper(const Helper& src) { std::cout << "copy\n"; }
Helper(Helper&& src) { std::cout << "move\n"; }
};
struct A
{
virtual ~A() {}
Helper h;
};
struct B
{
virtual ~B() = default;
Helper h;
};
struct C
{
Helper h;
};
int main()
{
{
A x;
A y(std::move(x)); // outputs "copy", because no move possible
}
{
B x;
B y(std::move(x)); // outputs "copy", because still no move possible
}
{
C x;
C y(std::move(x)); // outputs "move", because no user-declared dtor
}
}
+ g++-4.8 -std=c++11 -O2 -Wall -pthread main.cpp
+ ./a.out
コピー
コピー
移動
つまり、何も「失った」わけではありません。最初から移動機能はありませんでした。
以下は、どちらの場合も暗黙的なムーブ コンストラクターを禁止する標準的な一節です。
[C++11: 12.8/9]:
クラスの定義でX
ムーブ コンストラクターが明示的に宣言されていない場合は、次の場合にのみ、暗黙的にデフォルトとして宣言されます。
X
ユーザー宣言のコピー コンストラクターがありません。X
ユーザー宣言のコピー代入演算子がありません。X
ユーザー宣言の移動代入演算子がないX
ユーザー宣言のデストラクタがなく、かつ- 移動コンストラクターは、暗黙的に削除済みとして定義されません。
標準の将来のバージョンで、「ユーザー宣言」などの用語の正確な意味が実際にリストされていても問題ありません。少なくとも、これがあります:
[C++11: 8.4.2/4]:
[..]ユーザーが宣言し、最初の宣言で明示的にデフォルト設定または削除されていない場合、特別なメンバー関数はユーザー提供です。[..]
ここでの区別は、含意によって仮定することができます。
そのコメントは間違っています。
独自の move コンストラクターを提供する代わりに、コンパイラーに提供してもらいたい場合、必要条件の 1 つは、デストラクタもそれによって提供されること、つまり自明なデストラクタであることを期待することです。ただし、現在の標準では、ユーザーがデストラクタを指定する方法を受け入れる際に、暗黙的な実装をいつ提供できるかについてかなり厳密です。ユーザーによって宣言されたものはすべて、ユーザーが問題を自分の手に委ねていると見なされます。したがって、これだけではありません
~A() { … }
でもこれも
~A() = default;
コンパイラが暗黙のデストラクタを提供しないようにします。最初は定義であり、したがって宣言でもあります。2 番目は単なる宣言です。どちらの場合も、デストラクタはユーザーが宣言しているため、コンパイラが暗黙的な移動コンストラクタを提供することを禁止しています。
要件の背後にある理論的根拠は、移動中にオブジェクトのリソースが別のオブジェクトに移動され、元のオブジェクトが動的ストレージにリソースがない状態のままになることだと思います。ただし、クラスにそのようなリソースがない場合は、簡単に移動したり、破棄したりできます。重要なデストラクタを宣言すると、クラスで管理するリソースが些細なものではないというコンパイラの手がかりになります。ほとんどの場合、重要な移動も提供する必要があるため、コンパイラは提供しません。