21

私はこのブログを読んで、ゼロの法則が何を言っているのかを理解しようとしていました. IMO、独自のデストラクタを宣言する場合は、移動コンストラクタを作成し、割り当てをデフォルトとして移動することを忘れないでください。

class Widget {
public:
  ~Widget();         // temporary destructor
  ...                // no copy or move functions
};

「デストラクタの追加には、移動関数の生成を無効にするという副作用がありますが、ウィジェットはコピー可能であるため、移動を生成するために使用されたすべてのコードがコピーを生成するようになります。つまり、クラスにデストラクタを追加することで、おそらく-効率的な移動は、おそらく効率の悪いコピーに静かに置き換えられます。」

上記の Scott Meyers のテキストは、引用符の中にあり、いくつかの疑問が頭に浮かびます。

  • デストラクタを宣言すると移動セマンティクスが隠されるのはなぜですか?
  • デストラクタの宣言/定義は、移動セマンティクスまたはコピー コンストラクターとコピー代入のみを非表示にし、移動セマンティクスを非表示にしますか?
4

4 に答える 4

24

「ゼロのルール」は、実際には、どの特別なメンバー関数がいつ生成されるかということ以外の何かに関するものです。それは、クラスの設計に対する特定の態度についてです。質問に答えるように促します。

私のクラスはリソースを管理しますか?

その場合、各リソースを専用のクラスに移動して、クラスがリソースの管理のみを行う (他に何もしない) か、他のクラスの蓄積のみを行い、同じ論理タスクを実行する (ただしリソースを管理しない) ようにする必要があります。

これは、より一般的な単一責任の原則の特殊なケースです。

それを適用するとすぐに、リソース管理クラスの場合、移動コンストラクタ、移動代入、およびデストラクタを手動で定義する必要があることがすぐにわかります (コピー操作が必要になることはめったにありません)。また、リソース以外のクラスについては、移動 ctor/代入、コピー ctor/代入、デストラクタのいずれも宣言する必要はありません (実際、宣言する必要はありません)。

したがって、名前の「ゼロ」: クラスをリソース管理などに分離する場合、「その他」では特別なメンバー関数を提供する必要はありません (それらは正しく自動生成されます)。

C++ には、(特別なメンバー関数の) どの定義が他のどの定義を禁止するかという規則がありますが、それらはゼロの規則の核心を理解することからあなたをそらすだけです。

詳細については、次を参照してください。

  1. https://akrzemi1.wordpress.com/2015/09/08/special-member-functions/
  2. https://akrzemi1.wordpress.com/2015/09/11/declaring-the-move-constructor/
于 2015-11-26T07:49:45.903 に答える
3

Dtor の宣言/定義は移動セマンティクスのみを非表示にするか、またはコピー ctor/コピー割り当てと移動セマンティクスを非表示にしますか?

クラスにユーザー定義の移動コンストラクターが提供されていない場合、次のすべてが当てはまります。

  • ユーザー宣言のコピー コンストラクターがない
  • ユーザー宣言のコピー代入演算子はありません
  • ユーザー宣言の移動代入演算子はありません
  • ユーザーが宣言したデストラクタはありません

次に、コンパイラはムーブ コンストラクターをそのクラスの非明示的なインライン パブリック メンバーとして宣言し、signature T::T(T&&).

したがって、yesのコピー コンストラクターまたは代入演算子を宣言すると、暗黙的に宣言された移動コンストラクターも非表示になります。

于 2015-11-26T07:45:06.767 に答える
1

まず、マッツ・ピーターソンの答えは、その根拠に言及しているため、受け入れられたものよりも優れていると言えます。

第二に、補足として、もう少し詳しく説明したいと思います。

暗黙的に宣言された (またはデフォルト化された) move ctor の動作

c++draftから:

非共用体クラス X の暗黙的に定義されたコピー/移動コンストラクターは、その基底とメンバーのメンバーごとのコピー/移動を実行します。

コンパイラがムーブ ctor を暗黙的に宣言するときの条件

cppreferenceから:

クラスにユーザー定義の移動コンストラクターが提供されていない場合、次のすべてが当てはまります。

  • ユーザー宣言のコピー コンストラクターがない
  • ユーザー宣言のコピー代入演算子はありません
  • ユーザー宣言の移動代入演算子はありません
  • ユーザーが宣言したデストラクタはありません

次に、コンパイラはムーブ コンストラクターをそのクラスの非明示的なインライン パブリック メンバーとして宣言し、署名を付けますT::T(T&&)

dtor(および他の多くの)が暗黙的に宣言された移動ctorを防止するのはなぜですか?

上記の条件を見ると、ユーザー宣言されたデストラクタが暗黙的に宣言されたムーブ ctor を防止するだけでなく、ユーザー宣言されたコピー コンストラクター、ユーザー宣言されたコピー代入演算子、およびユーザー宣言されたムーブ代入演算子はすべて同じ防止効果を持ちます。

Mats Petersson が指摘したように、論理的根拠は次のとおりです。

移動操作でメンバーごとの移動以外の何かを行う必要があるとコンパイラが判断した場合、その必要がないと想定するのは安全ではありません。

  • ユーザーが宣言したデストラクタがある場合、つまりクリーンアップ作業が必要な場合は、おそらく移動元のオブジェクトでそれを実行する必要があります。

  • ユーザーが宣言した move 代入演算子がある場合、それもリソースを「移動」しているため、move ctor で同じことを行う必要があります。

  • ユーザー宣言のコピー コンストラクターまたはコピー代入演算子がある場合、これは最も興味深いケースです。move セマンティクスにより、パフォーマンスの最適化を実現しながら値のセマンティクスを維持できること、および move ctor が提供されていない場合、move はコピーに「フォールバック」することがわかっています。ある意味では、移動は「最適化されたコピー」と見なすことができます。したがって、コピー操作で何かを行う必要がある場合は、移動操作でも同様の作業が必要になる可能性があります。

上記の条件では、メンバーごとの移動以外の何かを実行する必要がある可能性があるため、コンパイラはそれが必要ないと想定せず、移動 ctor を暗黙的に宣言しません。

于 2018-05-23T14:08:35.787 に答える