C ++ 11で削除された関数が過負荷解決に参加するのはなぜですか?


87

C ++ 11でdeleted」関数が過負荷解決に参加するのはなぜですか?
なぜこれが便利なのですか?つまり、完全に削除されるのではなく、なぜ非表示になっているのでしょうか。



提案の理論的根拠を参照してくださいopen-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm#delete–
Jonathan Wakely

回答:


114

= delete構文の目的の半分は、人々が特定のパラメーターを使用して特定の関数を呼び出さないようにすることです。これは主に、特定のシナリオでの暗黙的な変換を防ぐためです。特定の過負荷を禁止するには、過負荷の解決に参加する必要があります。

あなたが引用する答えはあなたに完璧な例を与えます:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

場合はdelete完全に機能を削除し、それはなるだろう= deleteこれに構文相当します:

struct onlydouble2 {
  onlydouble2(double);
};

あなたはこれを行うことができます:

onlydouble2 val(20);

これは合法的なC ++です。コンパイラーはすべてのコンストラクターを調べます。それらのどれも直接整数型を取りません。しかし、そのうちの1つは、暗黙の変換後にそれを取ることができます。だからそれを呼ぶでしょう。

onlydouble val(20);

これは合法的なC ++ではありません。コンパイラーは、deleted個のコンストラクターを含むすべてのコンストラクターを調べます。via std::intmax_t(整数リテラルと完全に一致します)を介して完全一致が表示されます。したがって、コンパイラはそれを選択し、deleted関数を選択したため、すぐにエラーを発行します。

= delete「これは存在しない」という意味ではなく、「これを禁じる」という意味です。それははるかに強力な声明です。

C ++標準で= deleteが「これは存在しない」ではなく「これを禁止する」という意味である理由を尋ねていました

「これは存在しない」と言うのに特別な文法は必要ないからです。問題の特定の「これ」を単に宣言しないことによって、これを暗黙的に取得します。「私はこれを禁じます」は、特別な文法なしでは達成できない構成を表します。そのため、「これは禁止します」という特別な文法があり、他のことはありません。

明示的な「これは存在しません」という文法を使用することで得られる唯一の機能は、後で誰かがそれを存在すると宣言するのを防ぐことです。そして、それは独自の文法を必要とするほど有用ではありません。

それ以外の場合、コピーコンストラクターが存在しないことを宣言する方法はなく、その存在によって無意味なあいまいさが生じる可能性があります。

コピーコンストラクターは特別なメンバー関数です。すべてのクラスには常にコピーコンストラクタがあります。常にコピー代入演算子、ムーブコンストラクタなどがあるのと同じように。

これらの機能は存在します。問題は、それらを呼び出すことが合法であるかどうかだけです。それが= delete存在しないことを意味すると言おうとした場合、仕様は関数が存在しないことの意味を説明する必要があります。これは、仕様が扱う概念ではありません。

まだ宣言/定義されていない関数を呼び出そうとすると、コンパイラはエラーになります。ただし、「関数が存在しません」エラーではなく、未定義の識別子が原因でエラーが発生します(コンパイラがそのように報告した場合でも)。さまざまなコンストラクターはすべてオーバーロード解決によって呼び出されるため、それらの「存在」はその点で処理されます。

いずれの場合も、識別子を介して宣言された関数、またはコンストラクタ/デストラクタ(識別子を介して宣言された、型識別子のみ)があります。演算子のオーバーロードは、識別子をシンタックスシュガーの背後に隠しますが、それはまだ存在しています。

C ++仕様では、「存在しない関数」の概念を処理できません。過負荷の不一致を処理できます。過負荷のあいまいさを処理できます。しかし、そこに何がないのかはわかりません。したがって= delete、あまり有用ではない「この行を書いたことがないふりをする」よりも、はるかに有用な「これを失敗と呼ぶ試み」の観点から定義されます。

そしてもう一度、最初の部分を読み直してください。あなたはそれを行うことができないと、「機能が存在しません。」これがそのように定義されているもう1つの理由です。= delete構文の主なユースケースの1つは、ユーザーに特定のパラメータータイプの使用、明示的なキャストなどを強制できるようにするためです。基本的に、暗黙の型変換を阻止します。

あなたの提案はそれをしません。


1
@Mehrdad:= deleteが希望どおりに機能しない理由をさらに明確にする必要がある場合は、= deleteが持つべきと考える正確なセマンティクスをより明確にする必要があります。「この行を書いたことがないふりをする」べきですか?それとも何か他のものにする必要がありますか?
ニコルボーラス2012

1
いいえ、= delete「このメンバーは存在しません」という意味で、過負荷の解決に参加できなかったことを意味します。
user541686 2012

6
@Mehrdad:それは私の元のポイントに戻ります。それが、私が投稿した理由です。= delete「このメンバーが存在しない」という意味の場合、最初に投稿した例では、人々がonlydoubleのコンストラクターに整数を渡すのを防ぐことはできません。、onlydouble削除されるオーバーロードは存在しないため。オーバーロードの解決には関与しないため、整数の受け渡しを妨げることはありません。これは= delete構文のポイントの半分です。「この関数に暗黙的にXを渡すことはできません」と言うことができます。
ニコルボーラス2012

3
@Mehrdad:その論理では、なぜあなた=deleteはまったく必要なのですか?結局のところ、まったく同じこと、つまりコピーコンストラクタ/代入をプライベートとして宣言することで、「コピー不可」と言うことができます。また、プライベートなものを宣言しても、呼び出し不能になるわけではないことに注意してください。クラスのコードは引き続きそれを呼び出すことができます。したがって、と同じではありません= delete。いいえ、= delete構文を使用すると、以前は非常に不便で不可解だったことが、はるかに明白で合理的な方法で実行できます。
ニコルボーラス2012

2
@Mehrdad:後者が可能であるため:「宣言しないでください」と呼ばれます。その後、それは存在しません。プライベートを悪用するのではなく、オーバーロードを隠すための構文が必要な理由について...同じ効果を得るために他の機能を悪用するのではなく、何かを明示的に述べる手段が必要な理由を本当に尋ねていますか?何が起こっているのかをコードを読んでいる人には、より明白です。これにより、コードがユーザーにとって理解しやすくなり、記述が容易になると同時に、回避策の問題が修正されます。ラムダも必要ありません。
ニコルボーラス2012

10

C ++ワーキングドラフト2012-11-02は、このルールの背後にある理論的根拠を提供していません。いくつかの例だけです。

8.4.3削除された定義[dcl.fct.def.delete]
...
3 [:デフォルト以外の初期化と非整数初期化を強制することができます。

struct onlydouble {  
  onlydouble() = delete; // OK, but redundant  
  onlydouble(std::intmax_t) = delete;  
  onlydouble(double);  
};  

終了例]
[:特定の新しい式でクラスが使用されないようにするには、そのクラスに対して新しいユーザー宣言演算子の定義を削除します。

struct sometype {  
  void *operator new(std::size_t) = delete;  
  void *operator new[](std::size_t) = delete;  
};  
sometype *p = new sometype; // error, deleted class operator new  
sometype *q = new sometype[3]; // error, deleted class operator new[]  

終了例]
[:コピーコンストラクターとコピー代入演算子の削除された定義を使用し、次にムーブコンストラクターとムーブ代入演算子のデフォルト定義を提供することにより、クラスをコピー不可、つまり移動専用にすることができます。

struct moveonly {  
  moveonly() = default;  
  moveonly(const moveonly&) = delete;  
  moveonly(moveonly&&) = default;  
  moveonly& operator=(const moveonly&) = delete;  
  moveonly& operator=(moveonly&&) = default;  
  ~moveonly() = default;  
};  
moveonly *p;  
moveonly q(*p); // error, deleted copy constructor  

終了例]


4
例からすると、その論理的根拠は非常に明確に見えますね。
ジェシーグッド
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.