web-dev-qa-db-ja.com

=関数削除後の削除の意味

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

そのコンテキストで= deleteはどういう意味ですか?

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

213
Pat O'Keefe

関数の削除は 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
};
178
Prasoon Saurav
  1. = 0は、関数が純粋仮想であり、このクラスからオブジェクトをインスタンス化できないことを意味します。それから派生し、このメソッドを実装する必要があります
  2. = deleteは、コンパイラーがこれらのコンストラクターを生成しないことを意味します。私の知る限り、これはコピーコンストラクタと代入演算子でのみ許可されています。しかし、私は今後の標準があまり得意ではありません。
75
mkaes

The C++ Programming Language [第4版]-Bjarne Stroustrupの本からの抜粋は、=deleteを使用した本当の目的について語っています。

階層内のクラスにデフォルトのコピーまたは移動を使用するのは、通常、災害です。ベースへのポインタのみが与えられた場合、派生クラスがどのメンバーを持っているかわかりません(§3.2.2) 、したがって、それらをコピーする方法がわかりません。そのため、通常行うべき最善の方法は、デフォルトのコピーおよび移動操作を削除することです。つまり、これら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メカニズムは一般的です。つまり、任意の操作を抑制するために使用できます。

17
Saurav Sahu

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

他の誰もこの質問に答えていないようですので、=defaultもあることに言及する必要があります。

https://docs.Microsoft.com/en-us/cpp/cpp/explicitly-defaulted-and-deleted-functions#explicitly-defaulted-functions

7
Kyle Delaney

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

詳細に。

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

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

  }
};

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

3
ashutosh

私が取り組んできたコーディング標準には、ほとんどのクラス宣言について次のようなものがあります。

//  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
2785528

新しいC++ 0x標準。 N3242 working draft のセクション8.4.3を参照してください。

2
dubnde

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

(既存の回答への補遺)

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

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

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

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

end example

削除されたdefinitionを持つプライマリ関数テンプレートは特殊化できます

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

C++ 11の削除された関数の概念の前に、単純にプライマリ関数テンプレートの定義を省略することでこれを行うことができましたが、これはあいまいなundefined referenceを与えました間違いなく、プライマリ関数テンプレートの作成者から意味的な意図をまったく与えなかったエラー(意図的に省略されていますか?)。代わりにプライマリ関数テンプレートを明示的に削除すると、適切な明示的特化が見つからない場合のエラーメッセージはより良くなり、プライマリ関数テンプレートの定義の省略/削除が意図的であることも示します。

#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; */
}

より読みやすいエラーメッセージが生成され、削除の意図も明確に表示されます(undefined referenceエラーにより、開発者はこれを考えられない誤りと考える可能性があります) )。

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

#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
dfri

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

0
Tayyab