Aufruf der `constexpr` -Mitgliedsfunktion durch Referenz - clang vs gcc

8

Betrachten Sie das folgende Beispiel ( Snippet (0) ):

struct X
{
    constexpr int get() const { return 0; }
};

void foo(const X& x)
{
    constexpr int i = x.get();
}

int main()
{
    foo(X{});
}

Das obige Beispiel wird mit allen Versionen von g++vor g++ 10.xund nie unter kompiliert clang++. Die Fehlermeldung lautet:

error: 'x' is not a constant expression
    8 |     constexpr int i = x.get();
      |

Live-Beispiel auf godbolt.org

Der Fehler macht Sinn, da er xniemals ein konstanter Ausdruck im Körper ist von foo:

  • X::get()ist markiert constexprund hängt nicht vom Zustand ab x;

  • Durch Ändern const X&von const Xwird der Code mit jedem Compiler- Snippet ( auf godbolt.org) kompiliert (1) .


Noch interessanter wird es, wenn ich X::get()als static( (auf godbolt.org) Snippet (2) ) markiere . Mit dieser Änderung werden alle getesteten Versionen g++(einschließlich Trunk) kompiliert, während sie clang++immer noch nicht kompiliert werden können.

Also meine Fragen:

  • Ist es g++ 9.xrichtig, Snippet (0) zu akzeptieren ?

  • Sind alle Compiler korrekt darin, Snippet (1) zu akzeptieren ? Wenn ja, warum ist die Referenz von Bedeutung?

  • Sind g++ 9.xund g++ trunkrichtig in der Annahme von Snippet (2) ?

Vittorio Romeo
quelle
constexpr ist eine 'Maske' für einen Wert und eine const ist eine Variable, die Sie darüber informieren, dass sie nicht geändert wird. Werte können nicht referenziert oder angegeben werden, const jedoch. Das Funktionselement get () wird in der Kompilierungszeit verarbeitet. Mit anderen Worten, es ist in allen Fällen 0. Wie ich bereits sagte, können Werte nicht referenziert oder angegeben werden
TheArquitect
3
Ich denke, [expr.const] /2.11 gilt hier und xin fooist kein konstanter Ausdruck. Es gibt sogar einen alten (informell abgelehnten) Fehlerbericht über Clang wegen seines korrekten Verhaltens (während GCC einen tatsächlichen Fehler dafür hatte).
28.
3
Schrieb kürzlich darüber, scheint relevant: brevzin.github.io/c++/2020/02/05/constexpr-array-size
Barry
@Barry nice, in deinem Artikel hast du auch den Verweis auf den entsprechenden GCC-Fehlerbericht (ich konnte nur den abgelehnten Clang finden), der es gcc 9.x ermöglichte, Snippet 0 zu akzeptieren, das seitdem korrigiert wurde.
dfri

Antworten:

12

Ist g ++ 9.x korrekt beim Akzeptieren von Snippet (0)?

Nein.

Sind alle Compiler korrekt darin, Snippet (1) zu akzeptieren? Wenn ja, warum ist die Referenz von Bedeutung?

Ja, sind Sie.

Ein konstanter Ausdruck kann keinen ID-Ausdruck verwenden, der eine Referenz benennt, die keine vorherige Initialisierung eines konstanten Ausdrucks hat oder deren Lebensdauer während der Auswertung des konstanten Ausdrucks begonnen hat. [expr.const] /2.11 ( dasselbe in C ++ 20 )

Das Gleiche gilt nicht, wenn Sie eine Nichtreferenzvariable benennen, ohne dass eine Konvertierung von lWert in rWert erforderlich ist. x.get()wird nur xals lvalue bezeichnet und ruft nur eine constexprFunktion auf, die tatsächlich auf kein Mitglied von zugreift x, sodass kein Problem vorliegt .

Sind g ++ 9.x und g ++ Trunk korrekt beim Akzeptieren von Snippet (2)?

Nein, da der Ausdruck immer noch den Unterausdruck enthält, der xgegen die oben genannte Regel verstößt.

Nussbaum
quelle
Relevanter (informell abgelehnter) Fehlerbericht über Klirren , der auf die gleiche Richtigkeit von Klirren hinweist, die (immer) das Snippet 0 nicht kompiliert.
dfri