web-dev-qa-db-ger.com

Bedeutung von = löschen nach Funktionsdeklaration

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

Was macht = delete in diesem Zusammenhang bedeuten?

Gibt es andere "Modifikatoren" (außer = 0 und = delete)?

221
Pat O'Keefe

Das Löschen einer Funktion ist eine C++ 11-Funktion :

Die gebräuchliche Redewendung des "Kopierverbots" kann nun direkt ausgedrückt werden:

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

[...]

Der Mechanismus "Löschen" kann für jede Funktion verwendet werden. Zum Beispiel können wir eine unerwünschte Konvertierung wie folgt beseitigen:

struct Z {
    // ...

    Z(long long);     // can initialize with an long long         
    Z(long) = delete; // but not anything less
};
182
Prasoon Saurav
  1. = 0 bedeutet, dass eine Funktion rein virtuell ist und Sie kein Objekt aus dieser Klasse instanziieren können. Sie müssen daraus ableiten und diese Methode implementieren
  2. = delete bedeutet, dass der Compiler diese Konstruktoren nicht für Sie generiert. AFAIK dies ist nur auf Kopierkonstruktor und Zuweisungsoperator erlaubt. Aber ich bin nicht so gut im kommenden Standard.
76
mkaes

Dieser Auszug aus der C++ - Programmiersprache [4. Auflage] - Bjarne Stroustrup spricht über das eigentliche Ziel dahinter mit =delete:

Das Verwenden der Standardkopie oder -verschiebung für eine Klasse in einer Hierarchie ist normalerweise ein Katastrophe: Wenn nur ein Zeiger auf eine Basis angegeben wird, wissen wir einfach nicht, welche Mitglieder die abgeleitete Klasse hat (§3.2.2). Daher können wir nicht wissen, wie wir sie kopieren sollen . Daher ist es normalerweise am besten, die standardmäßigen Kopier- und Verschiebevorgänge zu löschen, dh die Standarddefinitionen dieser beiden Vorgänge zu entfernen:

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();
    // ...
};

Nun wird ein Versuch, eine Form zu kopieren, vom Compiler abgefangen.

Das =delete Mechanismus ist allgemein, das heißt, er kann verwendet werden, um jede Operation zu unterdrücken

20
Saurav Sahu

Gibt es andere "Modifikatoren" (außer = 0 und = delete)?

Da es so aussieht, als hätte niemand diese Frage beantwortet, sollte ich erwähnen, dass es auch =default.

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

7
Kyle Delaney

= delete ist eine in C++ 11 eingeführte Funktion. Nach =delete Diese Funktion darf nicht aufgerufen werden.

Im Detail.

Angenommen, in einer Klasse.

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

  }
};

Beim Aufrufen dieser Funktion für die Zuweisung von Objekten ist dies nicht zulässig. Bedeutet, dass der Zuweisungsoperator das Kopieren von einem Objekt auf ein anderes einschränken wird.

3
ashutosh

Die Codierungsstandards, mit denen ich gearbeitet habe, hatten für die meisten Klassendeklarationen die folgenden Eigenschaften.

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

Wenn Sie eine dieser 6 verwenden, kommentieren Sie einfach die entsprechende Zeile aus.

Beispiel: Klasse FizzBus benötigt nur dtor und benutzt daher nicht die anderen 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)

Wir kommentieren hier nur 1 aus und installieren die Implementierung woanders (wahrscheinlich dort, wo es der Kodierungsstandard vorschlägt). Die anderen 5 (von 6) sind mit delete nicht erlaubt.

Sie können auch '= delete' verwenden, um implizite Werbeaktionen mit Werten unterschiedlicher Größe zu verbieten ... Beispiel

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

Neuer C++ 0x Standard. Siehe Abschnitt 8.4.3 im N3242-Arbeitsentwurf

2
dubnde

Eine gelöschte Funktion ist implizit inline

(Nachtrag zu vorhandenen Antworten)

... und eine gelöschte Funktion soll die erste Deklaration der Funktion sein (mit Ausnahme des Löschens expliziter Spezialisierungen von Funktionsschablonen - das Löschen sollte bei der ersten Deklaration der Spezialisierung erfolgen), dh Sie können eine Funktion nicht deklarieren und später löschen, sagen wir bei seiner Definition lokal zu einer Übersetzungseinheit.

Zitieren [dcl.fct.def.delete]/4 :

Eine gelöschte Funktion ist implizit inline. ( Hinweis: Die Ein-Definition-Regel ( [basic.def.odr] ) gilt für gelöschte Definitionen. - Endnote] Eine gelöschte Definition einer Funktion ist die erste Deklaration der Funktion oder bei einer expliziten Spezialisierung einer Funktionsvorlage die erste Deklaration dieser Spezialisierung. [Beispiel:

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

- Beispiel beenden)

Eine primäre Funktionsvorlage mit einer gelöschten Definition kann spezialisiert werden

Obwohl eine allgemeine Faustregel lautet: m die Spezialisierung von Funktionsschablonen zu vermeiden da Spezialisierungen nicht am ersten Schritt der Überlastungslösung beteiligt sind, gibt es wohl einige Kontexte, in denen dies nützlich sein kann. Z.B. bei Verwendung einer nicht überladenen primären Funktionsvorlage ohne Definition für alle Typen, die nicht implizit in eine ansonsten konvertierungsbedingte Überladung konvertiert werden sollen; implizites Entfernen einer Anzahl impliziter Konvertierungsübereinstimmungen durch nur Implementieren exakter Typübereinstimmungen in der expliziten Spezialisierung der nicht definierten, nicht überlasteten Primärfunktionsschablone.

Vor dem Konzept der gelöschten Funktion von C++ 11 konnte man dies tun, indem man einfach die Definition der primären Funktionsvorlage wegließ. Dies ergab jedoch obskure undefinierte Referenz Fehler, die möglicherweise keinerlei semantische Absicht gaben der Autor der primären Funktionsvorlage (absichtlich weggelassen?). Wenn wir stattdessen die primäre Funktionsvorlage explizit löschen, werden die Fehlermeldungen für den Fall, dass keine geeignete explizite Spezialisierung gefunden wird, viel netter und zeigen auch, dass das Auslassen/Löschen der Definition der primären Funktionsvorlage beabsichtigt war.

#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< ...
}

Anstatt jedoch einfach eine Definition für die primäre Funktionsvorlage oben wegzulassen und einen unklaren undefinierten Referenzfehler zu erhalten, wenn keine explizite Spezialisierung zutrifft, kann die primäre Vorlagendefinition gelöscht werden:

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

Es wird eine besser lesbare Fehlermeldung ausgegeben, bei der die Löschabsicht ebenfalls klar erkennbar ist (wobei ein undefinierter Verweis Fehler dazu führen kann, dass der Entwickler dies für einen unüberlegten Fehler hält).

Zurück zu warum sollten wir diese Technik jemals anwenden wollen? Auch hier können explizite Spezialisierungen hilfreich sein, um implizit implizite Konvertierungen zu entfernen.

#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

Dies ist neu in C++ 0x-Standards, in denen Sie eine geerbte Funktion löschen können.

0
Tayyab