=関数宣言後に削除するという意味


241
class my_class
{
    ...
    my_class(my_class const &) = delete;
    ...
};

= deleteその文脈ではどういう意味ですか?

他の「修飾子」(= 0および以外= delete)はありますか?


23
@BlindyそれはC ++ 0xの標準、つまりすぐに。
Konrad Rudolph

1
私は正直に言って、このC ++ 0x機能を見逃していた。私はそれが#define0に評価され、それから隠し関数か何かを宣言するla Qtだと思っていました。
ブリンディ

同じまたは類似したものを意味する「disable」キーワードを思い出しました。想像できますか?または、それらの間に微妙な違いはありますか?
スチュワート

回答:


201

関数の削除はC ++ 11の機能です

「コピーを禁止する」という一般的な慣用句を直接表現できるようになりました。

class X {
    // ...
    X& operator=(const X&) = delete;  // Disallow copying
    X(const X&) = delete;
};

[...]

「削除」メカニズムは、任意の機能に使用できます。たとえば、次のような不要な変換を排除できます。

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};

3
copy-ctorとoperator =を "private"にするためだけに "コピーを禁止"する従来の方法ではありませんか?これは少し進んで、関数を生成しないようにコンパイラーに指示します。それらがプライベートであり、= deleteである場合、コピーは二重に禁止されていますか?
Reb.Cabin 2017

8
@Rebを使用=deleteすると、メソッドを見ることができるコンテキストprivate(つまり、クラスとそのフレンド内)からでもメソッドにアクセスできなくなります。これにより、コードを読んでいるときの不確実性がなくなります。@Prasoon、その2番目の例は、まだコンストラクターを削除しているだけです- operator long ()たとえば、削除されたものを見るとよいでしょう。
Toby Speight 2017

2
@ Reb.Cabin使い方が= delete良く使うよりprivate通常あなたがいるためか、他の類似のメカニズムたい禁断の機能が目に見えて宣言され、オーバーロードの解決などのために検討することが可能な限り早期に失敗し、ユーザーに明確なエラーを提供できるように、。宣言を「隠す」ことを含むソリューションは、この影響を減らします。
Leushenko、

1
コピーコンストラクターをパブリックにして、deleteキーワードを適用する特別な理由はありますか?コンストラクタをプライベートのままにして、キーワードを適用してみませんか?
Dohn Joe

81
  1. = 0関数が純粋な仮想であり、このクラスからオブジェクトをインスタンス化できないことを意味します。あなたはそれから派生し、このメソッドを実装する必要があります
  2. = deleteコンパイラーがそれらのコンストラクターを生成しないことを意味します。私の知る限り、これはコピーコンストラクタと代入演算子でのみ許可されています。しかし、私は次の標準にはあまり上手ではありません。

4
=delete構文には他にもいくつかの使い方があります。たとえば、これを使用して、呼び出しで発生する可能性のあるある種の暗黙的な変換を明示的に禁止できます。このためには、オーバーロードされた関数を削除するだけです。詳細については、C ++ 0xのWikipediaページをご覧ください。
LiKao

見つけたらすぐにやります。c ++ 0Xに追いつく時が
来たと思い

うん、C ++ 0xは素晴らしい。GCC 4.5以降が一般的になるのを待つことができないので、ラムダを使い始めることができます。
LiKao、2011

5
の説明= deleteは完全に正確ではありません。= deleteどの関数にも使用できます。その場合、削除済みとして明示的にマークされ、使用するとコンパイラエラーが発生します。特別なメンバー関数の場合、これは特に、コンパイラーによって生成されないことも意味しますが、これは削除された結果であり、= delete実際の結果ではありません。
MicroVirus

28

このC ++プログラミング言語[第4版]から抜粋-Bjarne Stroustrupの本は、使用の背後にある真の目的について述べてい=deleteます。

3.3.4操作の抑制

階層内のクラスにデフォルトのコピーまたは移動を使用することは、通常、惨事です。ベースへのポインターしか与えられていないので、派生クラスのメンバーがわからないので、それらをコピーする方法がわかりません。したがって、通常は、デフォルトのコピーおよび移動操作を削除すること、つまり、これら2つの操作のデフォルト定義を削除することが最善の方法です。

class Shape {
public:
  Shape(const Shape&) =delete; // no copy operations
  Shape& operator=(const Shape&) =delete;

  Shape(Shape&&) =delete; // no move operations
  Shape& operator=(Shape&&) =delete;
  ˜Shape();
    // ...
};

これで、Shapeをコピーしようとする試みがコンパイラーによってキャッチされます。

=delete機構は、任意の操作を抑制するために使用することができるされる、一般的です



5

私が使用したコーディング標準には、ほとんどのクラス宣言について次のものが含まれています。

//  coding standard: disallow when not used
T(void)                  = delete; // default ctor    (1)
~T(void)                 = delete; // default dtor    (2)
T(const T&)              = delete; // copy ctor       (3)
T(const T&&)             = delete; // move ctor       (4)
T& operator= (const T&)  = delete; // copy assignment (5)
T& operator= (const T&&) = delete; // move assignment (6)

これらの6のいずれかを使用する場合は、対応する行をコメント化するだけです。

例:FizzBu​​sクラスはdtorのみを必要とするため、他の5は使用しません。

//  coding standard: disallow when not used
FizzBuzz(void)                         = delete; // default ctor (1)
// ~FizzBuzz(void);                              // dtor         (2)
FizzBuzz(const FizzBuzz&)              = delete; // copy ctor    (3)
FizzBuzz& operator= (const FizzBuzz&)  = delete; // copy assig   (4)
FizzBuzz(const FizzBuzz&&)             = delete; // move ctor    (5)
FizzBuzz& operator= (const FizzBuzz&&) = delete; // move assign  (6)

ここでは1つだけコメントアウトし、その実装を他の場所(おそらくコーディング標準が示唆する場所)にインストールします。残りの5つ(6つのうち)は削除できません。

'= delete'を使用して、さまざまなサイズの値の暗黙的な昇格を禁止することもできます...例

// disallow implicit promotions 
template <class T> operator T(void)              = delete;
template <class T> Vuint64& operator=  (const T) = delete;
template <class T> Vuint64& operator|= (const T) = delete;
template <class T> Vuint64& operator&= (const T) = delete;

3

= deleteC ++ 11で導入された機能です。による=deleteと、その関数を呼び出すことはできません。

詳細に。

クラスで考えてみましょう。

Class ABC{
 Int d;
 Public:
  ABC& operator= (const ABC& obj) =delete
  {

  }
};

obj割り当てのためにこの関数を呼び出している間は許可されません。代入演算子は、あるオブジェクトから別のオブジェクトへのコピーを制限しようとしていることを意味します。


2

新しいC ++ 0x標準。N3242ワーキングドラフトのセクション8.4.3を参照してください


おっと、そのドラフトは時代遅れです。こちらが最新です(2011年4月3日現在):open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK

おかげで、リンクを更新しました。現在のドラフトを取得するのに非常に役立ちます。参照されたセクション/コンテンツは古いドラフトでも正しいため、反対票は理解できません。
dubnde

1

削除された関数は暗黙的にインラインです

(既存の回答の補遺)

...そして、削除された関数は関数の最初の宣言でなければなりません(関数テンプレートの明示的な特殊化を削除する場合を除いて-削除は特殊化の最初の宣言にある必要があります)。つまり、関数を宣言して後で削除することはできません。翻訳単位に対してローカルな定義で。

引用[dcl.fct.def.delete] / 4

削除された関数は暗黙的にインライン化されます。(注:削除された定義には、1つの定義ルール([basic.def.odr])が適用されます。— 末尾の注 ]関数の削除された定義は、関数の最初の宣言、または関数テンプレートの明示的な特殊化、その特殊化の最初の宣言[例:

struct sometype {
  sometype();
};
sometype::sometype() = delete;      // ill-formed; not first declaration

例を終了

定義が削除された一次機能テンプレートを特殊できます

特殊化はオーバーロード解決の最初のステップに参加しないため、一般的な経験則は関数テンプレートの特殊化を避けることですが、それが役立つコンテキストがいくつかあります。たとえば、定義のないオーバーロードされていないプライマリ関数テンプレートを使用して、暗黙的に変換によるマッチングによるオーバーロードに暗黙的に変換したくないすべてのタイプに一致させる場合。つまり、定義されていない、オーバーロードされていないプライマリ関数テンプレートの明示的な特殊化で、型の完全一致を実装するだけで、暗黙的な変換の一致の数を暗黙的に削除します。

C ++ 11の削除された関数の概念の前に、1次関数テンプレートの定義を単に省略することでこれを行うことができましたが、これにより、1次関数テンプレートの作成者から意味的な意図がまったくないと思われる曖昧な未定義の参照エラーが発生しました(意図的に省略) ?)。代わりに一次関数テンプレートを明示的に削除すると、適切な明示的な特殊化が見つからない場合のエラーメッセージがより良くなり、一次関数テンプレートの定義の省略/削除が意図的なものであったことも示されます。

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t);

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    //use_only_explicit_specializations(str); // undefined reference to `void use_only_explicit_specializations< ...
}

ただし、上記のプライマリ関数テンプレートの定義を単に省略するのではなく、明示的な特殊化が一致しない場合にあいまいな未定義の参照エラーを生成する代わりに、プライマリテンプレート定義を削除できます。

#include <iostream>
#include <string>

template< typename T >
void use_only_explicit_specializations(T t) = delete;

template<>
void use_only_explicit_specializations<int>(int t) {
    std::cout << "int: " << t;
}

int main()
{
    const int num = 42;
    const std::string str = "foo";
    use_only_explicit_specializations(num);  // int: 42
    use_only_explicit_specializations(str);
    /* error: call to deleted function 'use_only_explicit_specializations' 
       note: candidate function [with T = std::__1::basic_string<char>] has 
       been explicitly deleted
       void use_only_explicit_specializations(T t) = delete; */
}

削除の意図も明確に表示される、より読みやすいエラーメッセージを生成します(未定義の参照エラーにより、開発者はこれを思いがけない間違いと考える可能性があります)。

なぜ私たちがこのテクニックを使いたいのでしょうか?この場合も、明示的な特殊化は、暗黙的な変換を暗黙的に削除するのに役立ちます。

#include <cstdint>
#include <iostream>

void warning_at_best(int8_t num) { 
    std::cout << "I better use -Werror and -pedantic... " << +num << "\n";
}

template< typename T >
void only_for_signed(T t) = delete;

template<>
void only_for_signed<int8_t>(int8_t t) {
    std::cout << "UB safe! 1 byte, " << +t << "\n";
}

template<>
void only_for_signed<int16_t>(int16_t t) {
    std::cout << "UB safe! 2 bytes, " << +t << "\n";
}

int main()
{
    const int8_t a = 42;
    const uint8_t b = 255U;
    const int16_t c = 255;
    const float d = 200.F;

    warning_at_best(a); // 42
    warning_at_best(b); // implementation-defined behaviour, no diagnostic required
    warning_at_best(c); // narrowing, -Wconstant-conversion warning
    warning_at_best(d); // undefined behaviour!

    only_for_signed(a);
    only_for_signed(c);

    //only_for_signed(b);  
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = unsigned char] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */

    //only_for_signed(d);
    /* error: call to deleted function 'only_for_signed' 
       note: candidate function [with T = float] 
             has been explicitly deleted
       void only_for_signed(T t) = delete; */
}

0

これは、継承された関数を削除できるC ++ 0x標準の新しい機能です。


11
任意の関数を削除できます。例えば、void foo(int); template <class T> void foo(T) = delete;すべての暗黙の変換を停止します。intタイプの引数のみが受け入れられ、他のすべての引数は「削除された」関数をインスタンス化しようとします。
UncleBens 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.