In der Klasse statisch const ODR

8

Ich bin etwas verwirrt über die static In-Class Initialisierung eines const Members. Zum Beispiel im folgenden Code:

%Vor%

Live-Beispiel

Ich definiere nicht Foo::n (die Zeile ist kommentiert). Also, ich erwarte, dass der Aufruf f(Foo::n) zum Zeitpunkt der Verbindung fehlschlägt, und tatsächlich tut es das. Die folgende Zeile std::cout << g(Foo::n) << std::endl; kompiliert und verknüpft jedoch nur mit gcc (clang gibt immer noch einen Linker-Fehler aus), wenn ich ein Optimierungs-Flag wie -O1/2/3 verwende.

  1. Warum kompiliert gcc (versucht mit gcc5.2.0 und gcc 4.9.3) und verknüpft den Code, wenn die Optimierung aktiviert ist?
  2. Und ich bin richtig zu sagen, dass die einzige Verwendung von statischen statischen Konst-Membern in konstanten Ausdrücken ist, wie etwa Template-Parameter wie im h<Foo::n> -Aufruf, in welchem ​​Fall der Code sollte Link?
vsoftco 27.09.2015, 20:46
quelle

4 Antworten

4

ODR-Verletzungen erfordern keine Diagnose, aus dem Entwurf C ++ Standard-Standard-Abschnitt 3.2 [basic.def.odr] ( Schwerpunkt meiner Zukunft ):

  

Jedes Programm soll genau eine Definition von jedem Nicht-Inline enthalten   Funktion oder Variable, die in diesem Programm verwendet wird; keine Diagnose   erforderlich .

Daher ist inkonsistentes Verhalten auf verschiedenen Optimierungsstufen ein perfekt konformes Verhalten.

Informell ist eine Variable odr-used wenn:

  

seine Adresse wird genommen, oder eine Referenz ist daran gebunden, und eine Funktion wird odr verwendet, wenn ein Funktionsaufruf an sie erfolgt oder ihre Adresse genommen wird. Wenn ein Objekt oder eine Funktion odr-used ist, muss seine Definition irgendwo im Programm existieren; Eine Verletzung davon ist ein Link-Zeit-Fehler.

Also werden sowohl f als auch g odr-verwendet und erfordern eine Definition.

Das relevante C ++ 14-Zitat zu odr-use würde aus Abschnitt [basic.def.odr] lauten:

  

Eine Variable x, deren Name als potenziell bewerteter Ausdruck ex erscheint, ist odr - wird von ex verwendet, es sei denn wird angewendet   Die lvalue-to-rvalue-Konvertierung (4.1) nach x ergibt einen konstanten Ausdruck (5.19), der kein nichttriviales Argument aufruft   Funktionen und, wenn x ein Objekt ist, ist ex ein Element der Menge möglicher Ergebnisse eines Ausdrucks e,   wobei entweder die lvalue-to-rvalue-Konvertierung (4.1) auf e angewendet wird oder e ein Ausdruck mit verworfenem Wert ist [...]

Der Wortlaut in C ++ 11 ist ähnlich, die Änderungen von C ++ 11 nach C ++ 14 sind in Fehlerbericht 712 .

Vor C ++ 11 ist ein bisschen komplizierter, aber im Prinzip das gleiche für diesen Fall .

    
Shafik Yaghmour 27.09.2015, 22:14
quelle
4

Ich nehme an, dass der Compiler während der Optimierung die folgenden Aktionen ausführt:

  • Der Wert const static int n ist überall inline. Für die Variable n ist kein Speicher reserviert, Verweise darauf werden ungültig. Die Funktion f() benötigt einen Verweis auf n , damit das Programm nicht kompiliert wird.

  • Die Funktion g ist kurz und einfach. Es ist effektiv inline und optimiert. Nach der Optimierung benötigt die Funktion g keinen Verweis auf n , sondern gibt nur den konstanten Wert 42 zurück.

Die Lösung besteht darin, die Variable außerhalb der Klasse zu definieren:

%Vor%     
Andrey Nasonov 27.09.2015 21:05
quelle
4

Formal sind ODR-Verletzungen undefiniertes Verhalten , so dass der Compiler möglicherweise ein beliebiges Verhalten zeigt. Aus diesem Grund ändert sich das Verhalten mit der Optimierungsebene und dem Compiler - der Compiler ist nicht verpflichtet, ein bestimmtes Verhalten aufrechtzuerhalten.

    
Puppy 27.09.2015 21:33
quelle
2

Es gibt überhaupt keine Definition. GCC 4.9.2 kompiliert das nicht und verknüpft es mit irgendwelchen Flags.

Beachten Sie, dass:

%Vor%

ist eine Deklaration und Initialisierung , aber keine Definition .

    
Nevermore 27.09.2015 20:56
quelle