Was bestimmt, ob eine constexpr-Funktion ein konstanter Ausdruck ist?

Nov 30 2020

(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 incrementkein 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 ftatsä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

12 aschepler Nov 30 2020 at 22:23

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 fsie 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.

6 cigien Nov 30 2020 at 22:27

Da Sie keinen fkonstanten Ausdruck aufrufen , werden Sie gefragt, ob der Compiler eine Diagnose stellen muss, fdie nicht allein aufgrund seiner Definition in einem konstanten Ausdruck aufgerufen werden kann .

Die Anforderungen an die Definition einer constexprFunktion 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, fverstöß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 , constexprwie Funktionen fdas kann nicht in einem konstanten Ausdruck genannt werden, sind aber nicht diagnostizierbar als solche gelten als schlecht gebildete-no-Diagnose-erforderlich.

2 MarshallClow Nov 30 2020 at 22:22

Sie "rufen" fzur 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

Sneftel Nov 30 2020 at 22:12

Der Standard verlangt, dass eine constexprFunktion zur Kompilierungszeit für einige Parametersätze, aber nicht für alle, tatsächlich auswertbar ist . Es ist nicht erforderlich, dass Compiler eine constexprFunktion 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.