2017-12-07 9 views
7

Der folgende Code von GCC 7.2 und 5.0.0 Klappern akzeptiert wird, sondern wird von Microsoft VS 2017 15.5.0 Preview 5 und Intel C++ Kompilierer 19 zurückgewiesen:Lambda und dessen Argument als konstanter Ausdruck

struct S { }; 

constexpr int f(S) 
{ 
    return 0; 
} 

int main() 
{ 
    auto lambda = [](auto x) 
    { 
     constexpr int e = f(x); 
    }; 

    lambda(S{}); 
} 

Microsoft:

<source>(12): error C2131: expression did not evaluate to a constant 

Intel:

<source>(12): error: expression must have a constant value 
    constexpr int e = f(x); 
        ^
<source>(12): note: the value of parameter "x" (declared at line 10) cannot be used as a constant 
    constexpr int e = f(x); 
         ^

Wenn ich ersetzen f(x) mit f(decltype(x){}), beschweren sich sowohl Microsoft als auch Intel nicht. Ich verstehe, dass x ist kein konstanter Ausdruck, aber es wird nicht innerhalb f verwendet. Das ist wahrscheinlich der Grund, warum GCC und Clan sich nicht beschweren.

Ich denke, dass Microsoft und Intel Compiler diesen Code korrekt ablehnen. Was denken Sie?

+0

@RichardHodges, '-std = C++ 14'. – Evgeny

+0

OK jetzt denke ich, das ist ein Fehler von gcc und clang, da C++ 14 nicht mit constexpr Lambda kommt ... dies ist seit C++ 17 –

+0

@WF verfügbar, das Lambda ist absichtlich nicht "constexpr" (in echter Code ist es nicht). – Evgeny

Antwort

4

Von [expr.const]:

Ein Ausdruck e ist ein Kern konstanter Ausdruck es sei denn, die Auswertung von e, nach den Regeln der abstrakten Maschine, eine der folgenden Ausdrücke auswerten würde:

  • [...]]
  • eine L-Wert-zu-R-Wert Umwandlung, es sei denn, es zu

    • ein nichtflüchtiges glvalue von integralem oder Aufzählungstyp, der zu einem vollständigen nichtflüchtigen const Objekt verweist mit einer vorhergehenden Initialisierung angelegt wird, initialisiert mit ein konstanter Ausdruck, oder
    • ein nichtflüchtiges glvalue, die zu einem Unterobjekt eines Stringliteral oder
    • ein nichtflüchtiges glvalue bezieht, die mit constexpr definiert auf ein nicht flüchtiges Objekt bezieht, oder dass bezieht sich auf einen nicht einteilbares Unterobjekt eines solchen Objekts oder
    • ein nichtflüchtiger glvalue des literalen Typs, der sich auf ein nichtflüchtiges Objekt bezieht, dessen Lebensdauer mit der Auswertung von e begann;
  • [...]

In f(x), haben wir eine L-Wert-to-rvalue Umwandlung auf x. x ist kein Integral- oder Enumerationstyp, es ist kein Unterobjekt eines String-Literals, es ist kein Objekt, das mit consExpr definiert ist, und seine Lebensdauer hat nicht mit der Auswertung von f(x) begonnen.

Das scheint dies nicht zu einem konstanten Kernausdruck zu machen.

jedoch, wie Casey weist darauf hin, da S leer ist, nichts in seinem implizit generierte Kopierkonstruktor auslösen würde tatsächlich diese lvalue-zu-R-Wert-Konvertierung. Das würde bedeuten, dass nichts in diesem Ausdruck tatsächlich eine der Beschränkungen der konstanten Kernausdrücke verletzt, und daher sind gcc und clang korrekt, wenn sie dies akzeptieren. Diese Interpretation scheint mir richtig zu sein. constexpr macht Spaß.

+2

"In' f (x) ', machen wir eine Lvalue-to-rvalue-Konvertierung auf' x' "Ich denke, das ist falsch. Da 'S' leer ist, führt sein Kopierkonstruktor keine lvalue-to-rvalue-Konvertierung für den Quell-lvalue durch. Eine Kopie eines nicht konstanten Ausdrucks "S" kann immer noch ein konstanter Ausdruck sein (https://godbolt.org/g/m4jSgz). 'constexpr' ist manchmal verrückt. – Casey

+0

@Casey Ich denke, das ist eigentlich die richtige Interpretation. In der Tat verrückt. – Barry

2

Dies ist kein GCC/Clang Bug. Das gleiche Verhalten kann in C++ 11 mit einer Template-Funktion wiedergegeben werden:

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

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

on godbolt.org


Die Frage ist, gegeben ...

template <typename T> 
void foo(T x) 
{ 
    constexpr int e = f(x); 
} 

... ist f(x) ein konstanter Ausdruck?

Von [expr.const]:

Ein Ausdruck e ist ein Kern konstanter Ausdruck es sei denn, die Bewertung von e, nach den Regeln der abstrakten Maschine, würde eine der folgenden Ausdrücke auswerten:

  • Aufruf einer anderen Funktion als eines constexpr-Konstruktors für eine Literalklasse, einer constexpr-Funktion oder eines impliziten Aufrufs eines trivialen Destruktors

S{} und 0 sind konstante Ausdrücke, weil es keine der Regeln verstößt in [expr.const]. f(x) ist ein konstanter Ausdruck, da es sich um einen Aufruf an eine constexpr-Funktion handelt.

Wenn ich etwas nicht vermisse, sind gcc und clang hier richtig.