web-dev-qa-db-ja.com

デフォルトの移動コンストラクター/割り当ておよび削除されたコピーコンストラクター/割り当て

標準によると、

クラスXの定義が移動コンストラクターを明示的に宣言していない場合は、次の場合に限り、暗黙的にデフォルトとして宣言されます。

— Xにはユーザー宣言のコピーコンストラクターがありません。

— Xには、ユーザーが宣言したコピー代入演算子はありません。

— Xには、ユーザー宣言の移動代入演算子がありません。

— Xには、ユーザーが宣言したデストラクタはありません。

今、以下はコンパイルに失敗します

# include <utility>

class Foo
{
public:
  Foo() = default;
  Foo(Foo const &) = delete;
};

int main()
{
  Foo f;
  Foo g(std::move(f)); // compilation fails here
  return 0;
}

したがって、削除された関数はユーザー定義と見なされるようです。これは理にかなっています(デフォルトの実装ではありません)。しかし、その特定のケースでは、どのようにして削除されたコピーコンストラクタ/割り当ての混乱がデフォルトの移動コンストラクタ/割り当てを削除するでしょうか?

この問題は、手動で生成するため、実用的に重要だと思います。このようなデフォルト関数のメンテナンスはエラーが発生しやすいと同時に、クラスメンバーがコピー不可能なクラスを以前よりもはるかに一般的な獣にしたので、std::unique_ptrなどのクラスの使用の(正しい)増加です。

20
P-Gn

user-declaredは、user-provideddefineddefined by the user)、explicitly defaulted= default)またはexplicitly deleted= delete)とは対照的に、暗黙的にデフォルト/削除された(などあなたの移動コンストラクタとして)。

したがって、あなたのケースでは、yes移動コンストラクタがimplicitly削除されるため、コピーコンストラクターは明示的に削除されます(したがってuser-declared)。

ただし、その特定のケースでは、コピーコンストラクター/割り当ての混乱したデフォルトの移動コンストラクター/割り当てをどのように削除しますか?

そうではありませんが、標準はこのケースと複雑なケースを区別しません。

最も短い答えは、implicitly定義されたmove-constructorにexplicitly削除されたcopy-constructormight場合によっては危険です。user-definedデストラクタがあり、userがない場合も同様です。 -definedcopy-constructor( のルール/ 5 /ゼロ を参照)。これで、ユーザー定義のデストラクタがコピーコンストラクタを削除しないと主張できますが、これは単に言語の欠陥であり、多くの問題が発生するため削除できません。古い(悪い)プログラムの。 Bjarne Stroustrupを引用するには:

理想的な世界では、デフォルトとして「世代なし」を決定し、「通常の操作をすべて提供してください」という非常に単純な表記を提供すると思います。 [...]また、「デフォルトの操作なし」ポリシーはコンパイル時のエラー(簡単な修正方法があるはずです)につながりますが、デフォルトの生成ポリシーは実行時まで検出できない問題を引き起こします。

この詳細については N3174 = 10-0164 を参照してください。

ほとんどの人は /5 /ゼロのルール に従うことに注意してください。私の意見では、そうすべきです。デフォルトのmove-constructorを暗黙的に削除することにより、標準は間違いからユーザーを「保護」し、場合によってはcopy-constructorを削除することで以前から保護していたはずです(Bjarneの論文を参照)。

興味があればさらに読む:

この問題は、手動で生成するため、実用的に重要だと思います。このようなデフォルト関数のメンテナンスはエラーが発生しやすいと同時に、クラスメンバーがコピー不可能なクラスを以前よりもはるかに一般的な獣にしたので、std::unique_ptrなどのクラスの使用の(正しい)増加です。

Moveコンストラクタを明示的にデフォルトに設定すると、この問題が解決します。

class Foo {
public:
  Foo() = default;
  Foo(Foo const &) = delete;
  Foo(Foo&&) = default;
};

デフォルトの移動コンストラクターでコピー不可能なオブジェクトを取得します。私の意見では、これらの明示的な宣言は暗黙的な宣言よりも優れています(たとえば、コピーコンストラクターを削除せずに、移動コンストラクターをdefaultとして宣言するだけです)。

23
Holt

あなたが述べたように、§12.8から

クラスXの定義が移動コンストラクターを明示的に宣言していない場合は、次の場合に限り、暗黙的にデフォルトとして宣言されます。

  • Xにはser-declaredコピーコンストラクターがありません。

  • [...]

user-declaredに注意してください。しかし、§8.4.3を見ると:

次の形式の関数定義:

属性指定子シーケンスopt decl-specifier-seqopt 宣言子virt-specifier-seqopt =削除;

削除済みdefinitionと呼ばれます。 definitionが削除された関数は、削除された関数とも呼ばれます。

declare it以外で、削除された関数を暗黙的または明示的に参照するプログラムは、形式が正しくありません。

したがって、標準ではdeleted関数をser-declaredとして定義しています(上記からわかるように)。ただし、これらはdeletedであり、使用できません。

次に、§12.8によると、ユーザー宣言(= delete;付き)コピーコンストラクターがあるため、暗黙のmoveコンストラクターは定義されません。

2
Rakete1111