94

C++11 が" deleted" 関数をオーバーロード解決に参加させるのはなぜですか?
なぜこれが役立つのですか?言い換えれば、なぜそれらは完全に削除されるのではなく隠されているのでしょうか?

4

2 に答える 2

126

構文の目的の半分は、= 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 コンストラクターを含むすべてのコンストラクターを調べます。std::intmax_t(任意の整数リテラルと正確に一致します) を介して完全一致が見られます。したがって、コンパイラはそれを選択し、すぐにエラーを発行します。これは、deleted 関数が選択されたためです。

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

C++ 標準で = delete が「これは存在しません」ではなく「これを禁止します」と表示されている理由を尋ねていました。

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

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

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

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

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

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

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

C++ 仕様では、「存在しない関数」の概念を処理できません。オーバーロードの不一致を処理できます。オーバーロードのあいまいさを処理できます。しかし、そこにないものについては知りません。= deleteそのため、あまり役に立たない「この行を書いたことがないふりをする」よりも、はるかに有用な「これを失敗と呼ぼうとする試み」の観点から定義されます。

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

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

于 2012-12-29T20:37:14.683 に答える
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  

終了例]

于 2012-12-29T22:25:46.693 に答える