Ich bin etwas verwirrt über die static
In-Class Initialisierung eines const
Members. Zum Beispiel im folgenden Code:
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.
h<Foo::n>
-Aufruf, in welchem Fall der Code sollte Link? 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 .
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%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.
Tags und Links c++ gcc linker one-definition-rule