Was bestimmt, ob eine constexpr-Funktion ein konstanter Ausdruck ist?
(Der verwendete Compiler ist meines Wissens gcc mit c ++ 17 (in Visual Studio schwer zu finden))
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
increment( v );
return v;
}
int main( )
{
cout << f( ) << '\n';
}
Der obige Code gibt den Fehler beim Kompilieren aus:
Die constexpr-Funktion 'f' kann nicht zu einem konstanten Ausdruck führen.
Soweit ich weiß, liegt dies daran, dass die Funktion increment
kein constexpr ist. Was mich verwirrt ist, dass der folgende Code gut kompiliert wird:
#include <iostream>
using namespace std;
void increment( int& v )
{
++v;
}
int constexpr f()
{
int v = 0;
for( int i = 0; i < 1; ++i )
{
increment( v );
}
return v;
}
int main( )
{
cout << f( ) << '\n';
}
Dieser Code ist funktional derselbe und wird kompiliert, obwohl das Inkrement immer noch kein Constexpr ist. Ich verstehe nicht, wie es möglich ist, dass eine For-Schleife durch den Bereich [0, 1) den Compiler dazu bringt, zu erkennen, dass die Funktion f
tatsächlich ein constexpr ist.
Wenn jemand einige Einblicke in constexpr in c ++ und diese offensichtliche Inkonsistenz geben kann, würde ich es sehr schätzen.
Antworten
Beide Programme sind "schlecht geformt, keine Diagnose erforderlich", gemäß [dcl.constexpr] / 6 :
Für eine constexpr-Funktion oder einen constexpr-Konstruktor, der weder voreingestellt noch eine Vorlage ist, wenn keine Argumentwerte vorhanden sind, sodass ein Aufruf der Funktion oder des Konstruktors ein ausgewerteter Unterausdruck eines konstanten Kernausdrucks oder für einen Konstruktor ein ausgewerteter Unterausdruck von sein könnte Bei der vollständigen Initialisierung eines konstant initialisierten Objekts ( [basic.start.static] ) ist das Programm fehlerhaft , es ist keine Diagnose erforderlich.
Es ist ein bisschen seltsam, dass gcc das Problem mit dem zweiten Programm einfach nicht bemerkt, aber es passt immer noch.
Beachten Sie, dass eine Diagnose erforderlich wäre, wenn f
sie in einem Kontext verwendet würde, der beispielsweise tatsächlich einen konstanten Ausdruck erfordert constexpr int n = f();
.
Einige Dinge sind in einer constexpr-Funktion niemals erlaubt. Diese erfordern eine Diagnose (normalerweise eine Fehlermeldung), auch wenn die Funktion nie in einem konstanten Ausdruck verwendet wird - siehe die Antwort von cigien . Die fraglichen Programme verstoßen jedoch nicht gegen eine dieser strengeren Regeln.
Da Sie keinen f
konstanten Ausdruck aufrufen , werden Sie gefragt, ob der Compiler eine Diagnose stellen muss, f
die nicht allein aufgrund seiner Definition in einem konstanten Ausdruck aufgerufen werden kann .
Die Anforderungen an die Definition einer constexpr
Funktion sind hier aufgeführt :
Die Definition einer constexpr-Funktion muss die folgenden Anforderungen erfüllen:
(3.1) sein Rückgabetyp (falls vorhanden) muss ein wörtlicher Typ sein;
(3.2) Jeder seiner Parametertypen muss ein Literaltyp sein.
(3.3) Es darf keine Coroutine sein.
(3.4) Wenn die Funktion ein Konstruktor oder Destruktor ist, darf ihre Klasse keine virtuellen Basisklassen haben.
(3.5) sein Funktionskörper darf nicht einschließen
(3.5.1) eine goto-Anweisung,
(3.5.2) ein Identifikationsetikett,
(3.5.3) eine Definition einer Variablen vom nicht-wörtlichen Typ oder der statischen oder Thread-Speicherdauer.
Wie zu sehen ist, f
verstößt die Definition von nicht gegen eine der Anforderungen in der Liste. Ein Compiler passt sich also an, wenn er dies nicht diagnostiziert.
Wie bereits ausgeführt in Antwort des aschepler , constexpr
wie Funktionen f
das kann nicht in einem konstanten Ausdruck genannt werden, sind aber nicht diagnostizierbar als solche gelten als schlecht gebildete-no-Diagnose-erforderlich.
Sie "rufen" f
zur Kompilierungszeit nicht an.
Wenn Ihre Hauptfunktion enthalten wäre: static_assert(f() == 1, "f() returned 1");
Ich vermute, Sie würden den Fehler "f () ist kein konstanter Ausdruck" erhalten.
Hier ist eine verwandte Frage
Der Standard verlangt, dass eine constexpr
Funktion zur Kompilierungszeit für einige Parametersätze, aber nicht für alle, tatsächlich auswertbar ist . Es ist nicht erforderlich, dass Compiler eine constexpr
Funktion diagnostizieren , die bestimmte Dinge ausführt, die unter bestimmten Umständen nicht kompiliert werden können, oder ob eine solche Funktion über einen solchen Parametersatz verfügt. Dies vermeidet, dass sie das Halteproblem lösen müssen.