Seit den erweiterten Versionen von constexpr (glaube ich ab C ++ 14) können Sie constexpr-Funktionen deklarieren, die als "echter" constexpr verwendet werden könnten, dh der Code wird zur Kompilierzeit ausgeführt oder kann sich als Inline-Funktionen verhalten. Also wann kann dieses Programm haben:
%Vor%Das Ergebnis ist natürlich:
%Vor%so weit so gut. Meine Frage ist also: Gibt es einen Weg (möglicherweise Standard) in foo (const int s) zu wissen, ob die Funktion zur Kompilierzeit oder zur Laufzeit ausgeführt wird?
EDIT: Kann man zur Laufzeit auch wissen, ob eine Funktion zur Kompilierzeit ausgewertet wurde?
Die gelistete Technik funktioniert, aber da sie static_assert
verwendet, ist sie nicht sfinae freundlich. Ein besserer Weg (in der Theorie, Sie werden sehen, was ich meine), dies zu tun, ist zu überprüfen, ob eine Funktion noexcept
ist. Warum? Denn konstante Ausdrücke sind immer keine Ausnahmen, auch wenn die Funktionen nicht als solche gekennzeichnet sind. Betrachten Sie den folgenden Code:
test_helper
ist constexpr
, also wird es ein konstanter Ausdruck sein, solange sein Argument ist. Wenn es ein konstanter Ausdruck ist, wird es noexcept
sein, aber ansonsten wird es nicht sein (da es nicht als solches markiert ist).
Nun wollen wir das definieren:
%Vor% foo
ist nur noexcept
, wenn x
ein konstanter Ausdruck ist und b
wahr ist; Wenn der Boolesche Wert falsch ist, rufen wir eine nicht constexpr
-Funktion auf, die unseren Constexpr-Wert ruiniert. Also, lass es uns testen:
Es kompiliert, großartig! Dies gibt uns boolesche Kompilierzeit (keine Kompilierungsfehler), die beispielsweise für sfinae verwendet werden können.
Der Haken? Nun, Clang hat einen mehrjährigen Fehler und behandelt das nicht richtig. gcc jedoch tut es. Live-Beispiel: Ссылка . Es druckt "100", wie es sollte.
Ich denke, der übliche Weg dazu ist static_assert
. static_assert
s werden zur Kompilierzeit ausgewertet, so dass sie den Build unterbrechen, wenn ihre Bedingung falsch ist.
clang++ -std=c++14 so1.cpp
kompiliert sich für mich und zeigt, dass alles wie erwartet funktioniert.
Tut mir leid, dass ich die Party verpatzt habe, aber es gibt sicherlich keinen Standard Weg dies zu tun. Unter der Als-ob-Regel könnte der Compiler Code ausgeben, der das Ergebnis zur Laufzeit auch in solchen Fällen berechnet, in denen es bereits zur Kompilierzeit in einem anderen Kontext berechnet wurde. Alles, was zur Kompilierzeit getan werden kann, kann zur Laufzeit erneut ausgeführt werden, oder? Und die Berechnung wurde bereits bewiesen, nicht zu werfen.
Durch die Erweiterung kann also jede standardkonforme IS_REALLY_CONSTEXPR
oder is_really_constexpr
Prüfung nicht widerlegen, dass der exakt gleiche Aufruf oder der Wert des exakt gleichen constexpr
Symbols eine Laufzeitberechnung beinhaltet.
Natürlich gibt es in der Regel keinen Grund, eine Berechnung zur Laufzeit zu wiederholen, die zur Kompilierzeit durchgeführt werden kann oder auch schon gemacht wurde, aber es ging darum herauszufinden, ob der Compiler das vorberechnete Ergebnis verwendet, und es gibt keine 't eins.
Nun haben Sie möglicherweise Standard gesagt, also ist Ihre beste Wette vermutlich, eine der bereitgestellten Lösungen mit Ihrem Compiler Ihrer Wahl zu testen und zu hoffen, dass sie sich konsistent verhält. (Oder lesen Sie den Quellcode, wenn es offen / öffentliche Quelle ist und Sie so geneigt sind.)
Innerhalb einer constexpr
-Funktion können Sie nicht feststellen, ob Sie in einem constexpr
-Kontext ausgewertet werden. Es gab eine Reihe von Vorschlägen, um diese Funktionalität hinzuzufügen. Keine ist gelungen.
Außerhalb einer constexpr
-Funktion gibt es eine Reihe von Möglichkeiten, um zu bestimmen, ob ein Aufruf einer Funktion mit einem bestimmten Satz von Argumenten in einem constexpr
-Kontext ausgewertet wird. Am einfachsten wäre es, das Ergebnis in einem Kontext zu verwenden, der constexpr
erfordert.
Angenommen, Ihr constexpr-Ausdruck gibt ein nicht-void-Integral oder einen Pointer-Typ (einschließlich Funktionszeiger) zurück:
%Vor% then CONSTEXPR_EVAL( bar(foo, true) )
wird nicht kompiliert, wenn bar(foo, true)
zur Kompilierzeit nicht ausgewertet werden kann, und wenn es zur Kompilierungszeit ausgewertet werden kann, gibt es diesen Wert zurück.
Andere Tricks mit noexcept
(eine zur Kompilierzeit ausgewertete Funktion ist noexcept
) können funktionieren (siehe @ NirFriedmans Antwort ).