Warum lässt C ++ 11 " deleted" -Funktionen an der Überlastungsauflösung teilnehmen ?
Warum ist das nützlich? Oder mit anderen Worten, warum werden sie versteckt, anstatt vollständig gelöscht zu werden?
Warum lässt C ++ 11 " deleted" -Funktionen an der Überlastungsauflösung teilnehmen ?
Warum ist das nützlich? Oder mit anderen Worten, warum werden sie versteckt, anstatt vollständig gelöscht zu werden?
Antworten:
Die Hälfte des Zwecks der = deleteSyntax besteht darin, zu verhindern, dass Personen bestimmte Funktionen mit bestimmten Parametern aufrufen. Dies dient hauptsächlich dazu, implizite Konvertierungen in bestimmten Szenarien zu verhindern. Um eine bestimmte Überlastung zu verbieten, muss sie an der Überlastungsauflösung teilnehmen.
Die Antwort, die Sie zitieren, gibt Ihnen ein perfektes Beispiel:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Wenn deletedie Funktion vollständig entfernt würde, würde die = deleteSyntax dieser entsprechen:
struct onlydouble2 {
onlydouble2(double);
};
Sie könnten dies tun:
onlydouble2 val(20);
Dies ist legal C ++. Der Compiler betrachtet alle Konstruktoren. Keiner von ihnen nimmt direkt einen ganzzahligen Typ an. Aber einer von ihnen kann es nach einer impliziten Konvertierung nehmen. Also wird es das nennen.
onlydouble val(20);
Dies ist kein legales C ++. Der Compiler betrachtet alle Konstruktoren, einschließlich der deleted- Konstruktoren . Es wird eine genaue Übereinstimmung über angezeigt std::intmax_t(die genau mit jedem ganzzahligen Literal übereinstimmt). Der Compiler wählt es also aus und gibt sofort einen Fehler aus, da er eine deleted-Funktion ausgewählt hat.
= deletebedeutet "Ich verbiete das", nicht nur "Das existiert nicht". Es ist eine viel stärkere Aussage.
Ich habe gefragt, warum der C ++ - Standard sagt: = Löschen bedeutet "Ich verbiete dies" anstelle von "Dies existiert nicht".
Das liegt daran, dass wir keine spezielle Grammatik benötigen, um zu sagen, dass dies nicht existiert. Wir erhalten dies implizit, indem wir das betreffende "dies" einfach nicht deklarieren. "Ich verbiete das" stellt ein Konstrukt dar, das ohne spezielle Grammatik nicht erreicht werden kann. Wir bekommen also eine spezielle Grammatik, um zu sagen "Ich verbiete das" und nicht die andere Sache.
Die einzige Funktionalität, die Sie durch eine explizite Grammatik "Dies existiert nicht" erhalten würden, besteht darin, zu verhindern, dass jemand sie später für existent erklärt. Und das ist einfach nicht nützlich genug, um eine eigene Grammatik zu benötigen.
Andernfalls kann nicht erklärt werden, dass der Kopierkonstruktor nicht vorhanden ist, und seine Existenz kann zu unsinnigen Mehrdeutigkeiten führen.
Der Kopierkonstruktor ist eine spezielle Elementfunktion. Jede Klasse hat immer einen Kopierkonstruktor. So wie sie immer einen Kopierzuweisungsoperator, einen Verschiebungskonstruktor usw. haben.
Diese Funktionen existieren; Die Frage ist nur, ob es legal ist, sie anzurufen. Wenn Sie versuchen würden zu sagen, dass dies = deletebedeutet, dass sie nicht existieren, müsste die Spezifikation erklären, was es bedeutet, dass eine Funktion nicht existiert. Dies ist kein Konzept, das in der Spezifikation behandelt wird.
Wenn Sie versuchen, eine Funktion aufzurufen, die noch nicht deklariert / definiert wurde, tritt ein Fehler beim Compiler auf. Es tritt jedoch ein Fehler aufgrund eines undefinierten Bezeichners auf , nicht aufgrund eines Fehlers "Funktion existiert nicht" (selbst wenn Ihr Compiler dies so meldet). Verschiedene Konstruktoren werden alle durch Überlastungsauflösung aufgerufen, so dass ihre "Existenz" in dieser Hinsicht behandelt wird.
In jedem Fall gibt es entweder eine über den Bezeichner deklarierte Funktion oder einen Konstruktor / Destruktor (ebenfalls über den Bezeichner deklariert, nur einen Typbezeichner). Das Überladen von Operatoren verbirgt die Kennung hinter syntaktischem Zucker, ist aber immer noch vorhanden.
Die C ++ - Spezifikation kann das Konzept einer "nicht existierenden Funktion" nicht verarbeiten. Es kann eine Überlastungsfehlanpassung behandeln. Es kann eine Überlastungsmehrdeutigkeit behandeln. Aber es weiß nicht, was nicht da ist. So = deletewird in Bezug auf die weitaus nützlicher definiert „Versuch , dies zu nennen scheitern“ , anstatt die weniger nützlich „so tun , als ich nie diese Zeile geschrieben hat .“
Lesen Sie den ersten Teil noch einmal durch. Sie können dies nicht mit "Funktion existiert nicht" tun . Dies ist ein weiterer Grund, warum es so definiert wird: Einer der Hauptanwendungsfälle der = deleteSyntax besteht darin, den Benutzer zu zwingen, bestimmte Parametertypen zu verwenden, explizit umzuwandeln usw. Grundsätzlich implizite Typkonvertierungen vereiteln.
Ihr Vorschlag würde das nicht tun.
= delete, dass "dieses Mitglied existiert nicht" bedeutet, was bedeuten würde, dass es nicht an der Überlastungsauflösung teilnehmen könnte.
= delete"dieses Mitglied existiert nicht" gemeint ist, kann das erste Beispiel, das ich gepostet habe, nicht verhindern, dass Leute Ganzzahlen an onlydoubleden Konstruktor übergeben , da die onlydoublegelöschte Überladung nicht existieren würde . Es würde nicht an der Überlastungsauflösung teilnehmen und Sie daher nicht daran hindern, Ganzzahlen zu übergeben. Welches ist die Hälfte des Punktes der = deleteSyntax: um sagen zu können: "Sie können X nicht implizit an diese Funktion übergeben."
=deleteüberhaupt? Schließlich können wir "nicht kopierbar" sagen, indem wir genau dasselbe tun: den Kopierkonstruktor / die Zuweisung als privat deklarieren. Beachten Sie auch, dass das Deklarieren von etwas Privatem es nicht unanrufbar macht. Code innerhalb der Klasse kann ihn weiterhin aufrufen. Es ist also nicht dasselbe wie = delete. Nein, die = deleteSyntax ermöglicht es uns, etwas, das zuvor sehr unpraktisch und unergründlich war, viel offensichtlicher und vernünftiger zu machen.
Der C ++ Working Draft 2012-11-02 liefert keine Begründung für diese Regel, nur einige Beispiele
8.4.3 Gelöschte Definitionen [dcl.fct.def.delete]
...
3 [ Beispiel : Mit kann man eine nicht standardmäßige Initialisierung und eine nicht integrale Initialisierung erzwingen
struct onlydouble {
onlydouble() = delete; // OK, but redundant
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
- end example ]
[ Beispiel : Sie können die Verwendung einer Klasse in bestimmten neuen Ausdrücken verhindern, indem Sie gelöschte Definitionen eines vom Benutzer deklarierten Operators verwenden, der für diese Klasse neu ist.
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
- end example ]
[ Beispiel : Sie können eine Klasse nicht kopierbar machen, dh nur verschieben, indem Sie gelöschte Definitionen des Kopierkonstruktors und des Kopierzuweisungsoperators verwenden und dann Standarddefinitionen des Verschiebungskonstruktors und des Verschiebungszuweisungsoperators bereitstellen.
struct moveonly {
moveonly() = default;
moveonly(const moveonly&) = delete;
moveonly(moveonly&&) = default;
moveonly& operator=(const moveonly&) = delete;
moveonly& operator=(moveonly&&) = default;
~moveonly() = default;
};
moveonly *p;
moveonly q(*p); // error, deleted copy constructor
- Beispiel beenden ]