Ganz am Ende von Scott Schurrs Vortrag "Introducing constexpr
" auf der CppCon fragt er: "Gibt es eine Möglichkeit, eine Funktion zu vergiften?" Er erklärt dann, dass dies (wenn auch nicht auf standardmäßige Weise) getan werden kann durch:
- Einfügen
throw
einerconstexpr
Funktion - Ungelöst erklären
extern const char*
- Referenzierung der ungelösten
extern
in derthrow
Ich spüre, dass ich hier etwas überfordert bin, aber ich bin neugierig:
- Was bedeutet es, "eine Funktion zu vergiften"?
- Welche Bedeutung / Nützlichkeit hat die von ihm skizzierte Technik?
constexpr
Funktion zur Kompilierungszeit ausgewertet wird.constexpr
Funktion entweder zur Kompilierungszeit oder zur Laufzeit verwendet werden könnte. Dies ist also eine Möglichkeit, es zu erzwingen, damit Sie es zur Laufzeit nicht verwenden können? Wann ist das sinnvoll?constexpr
Funktion aufgrund der Einschränkungen häufig nicht die effizienteste Implementierung, sodass sie möglicherweise nicht zur Laufzeit ausgewertet werden soll. oder vielleicht ist es der Fehlerfall (wie in seinem Beispiel).Antworten:
Im Allgemeinen bezieht es sich darauf, eine Funktion unbrauchbar zu machen. Wenn Sie beispielsweise die Verwendung der dynamischen Zuordnung in einem Programm verbieten möchten, können Sie die
malloc
Funktion "vergiften" , sodass sie nicht verwendet werden kann.In dem Video verwendet er es auf eine spezifischere Art und Weise. Dies wird deutlich, wenn Sie die Folie lesen, die angezeigt wird, wenn er über die Vergiftung der Funktion spricht. Dort heißt es: "Eine Möglichkeit, nur die Kompilierungszeit zu erzwingen?"
Er spricht also davon, die Funktion zu "vergiften", um sie zur Laufzeit nicht mehr abzurufen, sodass sie nur in konstanten Ausdrücken aufgerufen werden kann. Die Technik besteht darin, einen Zweig in der Funktion zu haben, der beim Aufrufen in einem Kontext zur Kompilierungszeit niemals verwendet wird, und diesen Zweig etwas zu enthalten, das einen Fehler verursacht.
Ein
throw
Ausdruck ist in einer constexpr-Funktion zulässig, solange er während der Aufrufe der Funktion zur Kompilierungszeit nie erreicht wird (da Sie zur Kompilierungszeit keine Ausnahme auslösen können, handelt es sich um eine inhärent dynamische Operation wie das Zuweisen von Speicher). Ein Wurfausdruck, der sich auf ein undefiniertes Symbol bezieht, wird daher bei Aufrufen zur Kompilierungszeit nicht verwendet (da dies nicht kompiliert werden kann) und kann zur Laufzeit nicht verwendet werden, da das undefinierte Symbol einen Linkerfehler verursacht.Da das undefinierte Symbol in den Aufrufen der Funktion zur Kompilierungszeit nicht "odr-verwendet" wird, erstellt der Compiler in der Praxis keinen Verweis auf das Symbol, sodass es in Ordnung ist, dass es undefiniert ist.
Ist das nützlich? Er demonstriert, wie es geht, und sagt nicht unbedingt, dass es eine gute Idee oder allgemein nützlich ist. Wenn Sie es aus irgendeinem Grund tun müssen, kann seine Technik Ihr Problem lösen. Wenn Sie es nicht brauchen, müssen Sie sich darüber keine Sorgen machen.
Ein Grund dafür kann sein, dass die Version eines Vorgangs zur Kompilierungszeit nicht so effizient ist, wie sie sein könnte. Es gibt Einschränkungen hinsichtlich der Art der Ausdrücke, die in einer constexpr-Funktion zulässig sind (insbesondere in C ++ 11 wurden einige Einschränkungen in C ++ 14 entfernt). Sie haben also möglicherweise zwei Versionen einer Funktion zum Durchführen einer Berechnung, eine, die optimal ist, aber Ausdrücke verwendet, die in einer constexpr-Funktion nicht zulässig sind, und eine, die eine gültige constexpr-Funktion ist, aber bei einem Aufruf bei run- eine schlechte Leistung erbringt. Zeit. Sie können das suboptimale vergiften, um sicherzustellen, dass es niemals für Laufzeitanrufe verwendet wird, und um sicherzustellen, dass die effizientere Version (ohne Constexpr) für Laufzeitanrufe verwendet wird.
Hinweis: Die Leistung einer zur Kompilierungszeit verwendeten constexpr-Funktion ist nicht wirklich wichtig, da sie ohnehin keinen Laufzeitaufwand hat. Es kann Ihre Kompilierung verlangsamen, indem der Compiler zusätzliche Arbeit leistet, aber es entstehen keine Kosten für die Laufzeitleistung.
quelle
'Vergiftung' eines Bezeichners bedeutet, dass jeder Verweis auf den Bezeichner nach der 'Vergiftung' ein harter Compilerfehler ist. Diese Technik kann zum Beispiel für harte Verwerfungen verwendet werden (Funktion ist veraltet, verwenden Sie sie niemals!).
Im GCC gab es traditionell ein Pragma dafür :
#pragma GCC poison
.quelle