Warum den Konstruktor explizit löschen?

92

Wann / warum sollte ich meinen Konstruktor explizit löschen wollen? Angenommen, der Grund ist, seine Verwendung zu verhindern, warum nicht einfach machen private?

class Foo
{ 
  public: 
    Foo() = delete; 
};
Asche
quelle
14
Es passt irgendwie gut dazu = default, nicht einmal die Klasse kann es verwenden, und ich persönlich bevorzuge die Verwendung der gelöschten Funktion. over Function ist privat. Ersteres besagt ausdrücklich: "Dies ist nicht zur Verwendung gedacht." Wenn sich daraus etwas ergibt, macht die Klasse, die es nicht verwenden kann, tatsächlich einen semantischen Unterschied.
Chris
15
Ich denke ehrlich, dass die Leute mit den engen Abstimmungen langsam aggressiv werden. Ich sehe nicht, wie das nicht konstruktiv ist.
Luchian Grigore
4
@LuchianGrigore: Einverstanden. Ich habe mich gefragt, warum die Gemeinschaft selbst so viel starrer geworden ist. Ich verstehe den Punkt nicht.
Ed S.
11
Da ich C ++ 11 selten benutze, ist dies für mich informativer, als das OP wahrscheinlich überhaupt erkennt. Ich wusste nicht einmal, Sie könnten für einen Konstruktor markieren delete. Sowohl die Frage als auch Luchians Antwort können leicht als konstruktiv eingestuft werden. Jeder, der die Feinheiten von C ++ 11 nicht einatmet, aber bald muss, wird etwas aus beiden herausholen.
WhozCraig

Antworten:

86

Wie wäre es mit:

//deleted constructor
class Foo
{ 
  public: 
    Foo() = delete;     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //illegal
}

gegen

//private constructor
class Foo
{ 
  private: 
    Foo() {}     
  public:
    static void foo();
};

void Foo::foo()
{
   Foo f;    //legal
}

Das sind im Grunde verschiedene Dinge. privatesagt Ihnen, dass nur Mitglieder der Klasse diese Methode aufrufen oder auf diese Variable zugreifen können (oder natürlich auf Freunde). In diesem Fall ist es für eine staticMethode dieser Klasse (oder eines anderen Mitglieds) zulässig , einen privateKonstruktor einer Klasse aufzurufen . Dies gilt nicht für gelöschte Konstruktoren.

Probe hier .

Luchian Grigore
quelle
3
Sie müssen Foo () überhaupt nicht deklarieren, wenn Sie Foo (int) deklarieren. Foo () wird nicht generiert und daher ist Foo f sowieso ungültig. Ihr Beispiel zeigt also nicht den Fall für einen gelöschten Konstruktor. Überzeugen Sie sich
markieren Sie den
1
@mark Ich habe 2 Konstruktoren geschrieben, um den Punkt zu beweisen. Ich werde bearbeiten, damit es allen klar ist.
Luchian Grigore
1
Ich verstehe den Unterschied, ich verstehe nur nicht den Mehrwert der delete-Anweisung im Allgemeinen und für einen Konstruktor im Besonderen. Immerhin könnte ich einen privaten Standardkonstruktor ohne den Body angeben. Dann schlägt auch der Code nur während der Verknüpfung fehl. Nun, ich kann sehen, dass Löschen die Absicht expliziter vermittelt, aber das war es auch schon.
Markieren Sie den
11
@mark Ja, das wäre die C ++ 98-Methode. Aber meiner Meinung nach ist es eine sehr wichtige Sache bei der Programmierung im Allgemeinen, die Absicht klar zu vermitteln. In diesem Fall sehen einige Leser möglicherweise einen privaten undefinierten Konstruktor und nehmen an, dass er zufällig ist, und fügen einfach eine Definition hinzu, insbesondere wenn die Definition so trivial ist wie ein Standardkonstruktor (Ja, ein Kommentar hilft, aber wir würden den Compiler sehr bevorzugen -Durchsetzung über die Durchsetzung von Kommentaren). Wenn wir unsere Absicht klarer machen, erhalten wir auch eine viel bessere Fehlermeldung, die "explizit gelöscht" anstelle von "undefinierter Referenz" lautet.
Park
2
Ich verstehe ehrlich gesagt nicht, wie dies die Hauptfrage beantwortet. Die Frage im Titel und die erste Frage von OP im Beitrag lauteten: Wann / warum sollte ich meinen Konstruktor explizit löschen wollen?
Alexander Bolinsky
11

Warum den Konstruktor explizit löschen?

Ein weiterer Grund:
Ich verwende, deletewenn ich sicherstellen möchte, dass eine Klasse mit einem Initialisierer aufgerufen wird. Ich halte es für einen sehr eleganten Weg, dies ohne Laufzeitprüfungen zu erreichen.

Der C ++ - Compiler führt diese Prüfung für Sie durch.

class Foo
{
   public:
       Foo() = delete;
       Foo(int bar) : m_bar(bar) {};
   private:
       int m_bar;
}

Dieser - sehr vereinfachte - Code stellt sicher, dass es keine solche Instanziierung gibt:Foo foo;

Peter VARGA
quelle
12
Die gelöschte Deklaration ist hier nicht erforderlich. Es wird automatisch mit jedem vom Benutzer bereitgestellten Konstruktor gelöscht
Mike Lui
5
Um den Kommentar von @MikeLui zu verdeutlichen, ist die gelöschte Deklaration für den Compiler nicht erforderlich . Es gibt zahlreiche Fälle, in denen Code wie dieser enthalten sein sollte, um anderen Programmierern die Absicht zu erklären .
Jeff G
Zusammen mit der Erklärung Ihrer Absicht wird ein offensichtlicher Ort geschaffen, an dem Sie Ihren Grund für das Löschen in der öffentlichen Benutzeroberfläche dokumentieren können. Außerdem ist der Compilerfehler so kurz wie "Verwendung der gelöschten Funktion". Wenn Foozahlreiche Konstruktoren vorhanden Foo foo;wären , nur keine Standardkonstruktoren, würde dies zu einem viel längeren Fehler führen, in dem alle implizit definierten, geschützten und privaten Konstruktoren aufgelistet werden, die nicht übereinstimmen.
Sigma
Ich verstehe immer noch nicht, wie eine zusätzliche Zeile mit der Deklaration des Konstruktors, dessen Schlüsselwort "= delete" die Absicht der Idee "kein Standardkonstruktor" deklariert, besser ist als ... nur kein Standardkonstruktor? Beispiel: Ich möchte die Variable "a" in meinem Code nicht deklarieren - was ist besser, um "// int a; // keine Definition der Variablen a" zu schreiben oder einfach nichts über diese Variable in den Code zu schreiben?
Ezh
2

Ich habe Standard-Ctors getroffen, die im Quellcode von LLVM als "gelöscht" deklariert wurden (zum Beispiel in AlignOf.h). Die zugehörigen Klassenvorlagen befinden sich normalerweise in einem speziellen Namespace namens 'llvm :: detail'. Ich denke, der ganze Zweck dort war, dass sie diese Klasse nur als Hilfsklasse betrachteten. Sie hatten nie vor, sie zu instanziieren; Nur um sie im Kontext anderer Klassenvorlagen mit einigen Metaprogrammier-Tricks zu verwenden, die in der Kompilierungszeit ausgeführt werden.

Z.B. Es gibt diese AlignmentCalcImpl-Klassenvorlage, die nur in einer anderen Klassenvorlage namens AlignOf als Parameter für den Operator sizeof (.) verwendet wird. Dieser Ausdruck kann in der Kompilierungszeit ausgewertet werden. und es ist nicht erforderlich, die Vorlage zu instanziieren -> warum also nicht den Standard-Ctor-Löschvorgang deklarieren, um diese Absicht auszudrücken?

Aber es ist nur meine Annahme.

Gybacsi
quelle