2017-10-01 3 views
2

das folgende Code-Snippet vor:Undefiniertes Verhalten, wenn constexpr-negative Bitverschiebung auswertet?

int main(){ 
    constexpr int x = -1; 
    if(x >= 0){ 
     constexpr int y = 1<<x; 
    } 
} 

GCC 7 (und wahrscheinlich auch andere Versionen von GCC) weigert sich, dies zu kompilieren und sagt:

error: right operand of shift expression '(1 << -1)' is negative [-fpermissive] 

Ich kann wo denke, das herkommen kann: Die constexpr Deklaration auf y macht GCC y zur Kompilierzeit auswerten, wo es negativ sein könnte. Durch das Entfernen der constexpr wird der Fehler behoben.

Ist dieses undefinierte Verhalten jedoch vom Standard? Die Bedingung ist immer falsch, daher wird der Wert y niemals verwendet.

In meinem tatsächlichen Code ist x ein Vorlagenparameter, der negativ sein kann oder nicht.

+0

Können Sie 'if conexpr' verwenden? – MikeMB

+0

@MikeMB - Theoretisch könnte das funktionieren. [Aber GCC gibt immer noch den gleichen Fehler aus] (http://coliru.stacked-crooked.com/a/5302256c8fb5ccda). Wahrscheinlich ein Compiler Bug. – StoryTeller

+0

Negative Linksverschiebung ist undefiniertes Verhalten.Während der Laufzeit wäre dies kein Problem, da die Bedingung dies verhindert. Aber genau wie Sie gesagt haben, muss der Compiler den Ausdruck während der Kompilierzeit auswerten und muss einen Fehler für jede Instanz von UB generieren. – MikeMB

Antwort

4

GCC beschwert sich, weil Ihre Definition von y explizit eine schlecht geformte constexpr Deklaration ist. Der initialzier verletzt [expr.const]/2, die angibt:

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

  • eine Operation, die nicht definiertes Verhalten wie angegeben haben würde in den Abschnitten [Intro] bis [cpp] diese Internationale Norm [Anmerkung: einschließlich zum Beispiel Signed integer ove rflow (Klausel [expr]), bestimmte Zeigerarithmetik ([expr.add]), Division durch Null, oder bestimmte Schichtoperationen - Endnote];

So können Sie nicht 1<<x verwenden y zu initialisieren. Es spielt keine Rolle, dass die Verzweigung niemals ausgeführt wird und eliminiert werden kann. GCC ist immer noch verpflichtet, zu überprüfen, ob es semantisch korrekt ist.

2

Genau wie StoryTeller erklärte, ist dies das erwartete Verhalten, da linkes Verschieben um eine negative Zahl ein undefiniertes Verhalten ist und ein Ausdruck, der UB ergibt, nicht in einem konstanten Kernausdruck verwendet werden kann (die Tatsache, dass Sie es nicht versuchen Der Zugriff auf das Ergebnis dieses Ausdrucks während der Laufzeit ändert nicht die Tatsache, dass der Compiler während der Kompilierungszeit den Compiler auswerten muss.

Wenn Ihr Zweig tatsächlich auf einem Template-Parameter abhängig können Sie dieses Problem umgehen, indem if constexpr mit:

template<int x> 
constexpr int foo() { 
    if constexpr (x >= 0) { 
     constexpr int y = 1 << x; 
     return y; 
    } 
    return 0; 
} 

Edit: Wie die Antworten auf StoryTeller's question erklären, dies funktioniert nur innerhalb einer Vorlage und nur dann, wenn die bedingte abhängt auf dem Vorlagenparameter (ausführlichere Erklärung in den Antworten).

Verwandte Themen