Bedeutung von = Löschen nach Funktionsdeklaration

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

Was bedeutet = deletedas in diesem Zusammenhang?

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

Pat O'Keefe
quelle
23
@Blindy Es wird Standard in C ++ 0x sein, dh bald.
Konrad Rudolph
1
Ich stehe korrigiert da, ich hatte diese C ++ 0x Funktion verpasst. Ich dachte, es wäre ein #definea la Qt, das mit 0 bewertet und dann eine versteckte Funktion oder so deklariert wurde.
Blindy
Ich erinnere mich an ein Schlüsselwort "Deaktivieren", das dasselbe oder ähnliches bedeutet. Stell ich es mir vor? Oder gibt es einen subtilen Unterschied zwischen ihnen?
Stewart

Antworten:

201

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

Die übliche Redewendung "Kopierverbot" kann nun direkt ausgedrückt werden:

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

[...]

Der "Lösch" -Mechanismus 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
};
Prasoon Saurav
quelle
3
Ist es nicht die traditionelle Methode, das Kopieren zu "verbieten", nur um den Kopierer und Operator = "privat" zu machen? Dies geht etwas weiter und weist den Compiler an, die Funktionen nicht einmal zu generieren. Wenn sie beide privat und = löschen sind, ist das Kopieren doppelt verboten?
Reb.Cabin
8
@Reb =deletemacht die Methode auch in Kontexten unzugänglich, in denen privateMethoden angezeigt werden (dh innerhalb der Klasse und ihrer Freunde). Dies beseitigt jegliche Unsicherheit beim Lesen des Codes. @Prasoon, in diesem zweiten Beispiel werden immer noch nur Konstruktoren gelöscht - es wäre schön, wenn beispielsweise ein Konstruktor gelöscht würde operator long ().
Toby Speight
2
@ Reb.Cabin Verwendung = deleteist besser als die Verwendung privateoder andere ähnliche Mechanismen , weil Sie in der Regel wollen die verbotene Funktion sichtbar deklarieren und so für die Überladungsauflösung usw., berücksichtigt wird , dass es so früh wie möglich ausfallen kann und die klarste Fehler für den Benutzer zur Verfügung stellen. Jede Lösung, bei der die Deklaration "ausgeblendet" wird, verringert diesen Effekt.
Leushenko
1
Gibt es einen besonderen Grund, den Kopierkonstruktor öffentlich zu machen und das Schlüsselwort delete anzuwenden? Warum lassen Sie den Konstruktor nicht privat und wenden das Schlüsselwort an?
Dohn Joe
81
  1. = 0bedeutet, 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. = deletebedeutet, dass der Compiler diese Konstruktoren nicht für Sie generiert. AFAIK Dies ist nur für den Kopierkonstruktor und den Zuweisungsoperator zulässig. Aber ich bin nicht so gut im kommenden Standard.
mkaes
quelle
4
Es gibt einige andere Verwendungen der =deleteSyntax. Sie können es beispielsweise verwenden, um implizite Konvertierungen, die möglicherweise mit dem Aufruf stattfinden, explizit zu verbieten. Dazu löschen Sie einfach die überladenen Funktionen. Weitere Informationen finden Sie auf der Wikipedia-Seite zu C ++ 0x.
LiKao
Ich werde das tun, sobald ich welche finde. Schätze, es ist Zeit, c ++ 0X
mkaes
Ja, C ++ 0x rockt. Ich kann es kaum erwarten, dass GCC 4.5+ häufiger auftritt, damit ich Lambdas verwenden kann.
LiKao
5
Die Beschreibung für = deleteist nicht ganz korrekt. = deletekann für jede Funktion verwendet werden. In diesem Fall wird sie explizit als gelöscht markiert und jede Verwendung führt zu einem Compilerfehler. Für spezielle Elementfunktionen bedeutet dies insbesondere auch, dass sie dann nicht vom Compiler für Sie generiert werden. Dies ist jedoch nur ein Ergebnis des Löschens und nicht das, was = deletewirklich ist.
MicroVirus
28

Dieser Auszug aus dem Buch The C ++ Programming Language [4. Ausgabe] - Bjarne Stroustrup beschreibt den wahren Zweck der Verwendung von =delete:

3.3.4 Operationen unterdrücken

Die Verwendung der Standardkopie oder -verschiebung für eine Klasse in einer Hierarchie ist normalerweise eine Katastrophe : Wenn wir nur einen Zeiger auf eine Basis haben, wissen wir einfach nicht, welche Mitglieder die abgeleitete Klasse hat, und können daher nicht wissen, wie sie kopiert werden sollen . Das Beste, was Sie tun können, ist normalerweise, die Standardoperationen zum Kopieren und Verschieben zu löschen, dh die Standarddefinitionen dieser beiden Operationen 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();
    // ...
};

Jetzt wird der Versuch, eine Form zu kopieren, vom Compiler abgefangen.

Der =deleteMechanismus ist allgemein, dh er kann verwendet werden, um jede Operation zu unterdrücken

Saurav Sahu
quelle
5

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

//  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 verwendet 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 an anderer Stelle (wahrscheinlich dort, wo der Codierungsstandard dies vorschlägt). Die anderen 5 (von 6) sind beim Löschen nicht zulässig.

Sie können auch '= löschen' verwenden, um implizite Werbeaktionen mit unterschiedlich großen Werten 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;
2785528
quelle
3

= deleteist eine in C ++ 11 eingeführte Funktion. Gemäß =deletewird diese Funktion aufrufen , nicht erlaubt.

Im Detail.

Angenommen, in einer Klasse.

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

  }
};

Beim Aufrufen dieser Funktion zur Objektzuweisung ist dies nicht zulässig. Bedeutet, dass der Zuweisungsoperator das Kopieren von einem Objekt auf ein anderes einschränken wird.

Ashutosh
quelle
2

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

dubnde
quelle
Whoa, dieser Entwurf ist veraltet. Hier ist die neueste (Stand: 3. April 2011): open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
TonyK
Danke und habe den Link aktualisiert. Sehr hilfreich, um den aktuellen Entwurf zu erhalten. Der Abschnitt / Inhalt, auf den verwiesen wird, war auch im alten Entwurf korrekt, daher verstehe ich die Abwärtsabstimmung nicht.
Dubnde
1

Eine gelöschte Funktion ist implizit inline

(Nachtrag zu bestehenden Antworten)

... Und eine gelöschte Funktion ist die erste Deklaration der Funktion (außer zum Löschen expliziter Spezialisierungen von Funktionsvorlagen - das Löschen sollte bei der ersten Deklaration der Spezialisierung erfolgen), dh Sie können eine Funktion nicht deklarieren und später beispielsweise löschen. bei seiner Definition lokal zu einer Übersetzungseinheit.

Zitieren von [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 Definition einer Funktion gelöscht wird die erste Erklärung der Funktion oder für eine explizite Spezialisierung einer Funktionsvorlage sein , die erste Erklärung 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 , Spezialisierungsfunktionsvorlagen zu vermeiden, da Spezialisierungen nicht am ersten Schritt der Überlastungsauflösung teilnehmen, gibt es einige Kontexte, in denen dies nützlich sein kann. Beispiel: Wenn Sie eine nicht überladene primäre Funktionsvorlage ohne Definition verwenden, um allen Typen zu entsprechen, die nicht implizit in eine ansonsten durch Konvertierung übereinstimmende Überladung konvertiert werden sollen. dh implizite Entfernung einer Anzahl von impliziten Konvertierungsübereinstimmungen, indem nur exakte Typübereinstimmungen in der expliziten Spezialisierung der nicht definierten, nicht überladenen primären Funktionsvorlage implementiert werden.

Vor dem gelöschten Funktionskonzept von C ++ 11 konnte man dies tun, indem man einfach die Definition der primären Funktionsvorlage wegließ, aber dies ergab obskure undefinierte Referenzfehler , die dem Autor der primären Funktionsvorlage wohl keine semantische Absicht gaben (absichtlich weggelassen) ?). Wenn wir stattdessen die primäre Funktionsvorlage explizit löschen, werden die Fehlermeldungen, falls keine geeignete explizite Spezialisierung gefunden wird, viel schöner 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 obige primäre Funktionsvorlage wegzulassen und einen obskuren undefinierten Referenzfehler zu erhalten, wenn keine explizite Spezialisierung übereinstimmt, 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 angezeigt, bei der auch die Löschabsicht deutlich sichtbar ist (bei der ein undefinierter Referenzfehler dazu führen kann, dass der Entwickler dies für einen unüberlegten Fehler hält).

Zurück zu dem Grund, warum wir diese Technik jemals anwenden wollen? Auch hier können explizite Spezialisierungen hilfreich sein, um implizite Konvertierungen implizit 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; */
}
dfri
quelle
0

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

Tayyab
quelle
11
Sie können jede Funktion löschen. Zögert beispielsweise void foo(int); template <class T> void foo(T) = delete;alle impliziten Konvertierungen. Es werden nur Argumente vom intTyp akzeptiert, alle anderen versuchen, eine "gelöschte" Funktion zu instanziieren.
Onkel Bens