Was darf ich mit einem statischen, in der Klasse initialisierten consExpr-Datenelement tun?

8

Dies ist wahrscheinlich eine etwas ungewöhnliche Frage, da es eine ausführlichere Erklärung für eine kurze Antwort auf eine andere Frage und einige Aspekte des C ++ 11 Standards, die damit zusammenhängen.

Um die Bezugnahme zu erleichtern, möchte ich die hier angesprochene Frage zusammenfassen. Das OP definiert eine Klasse:

%Vor%

und wundern sich, warum er keinen Fehler über seine Verwendung eines in der Klasse initialisierten statischen Datenelements erhält (ein Buch erwähnte dies als illegal). In Johannes Schaubs Antwort heißt es:

  1. Dies verstößt gegen die Eine Definitionsregel ;
  2. Keine Diagnose erforderlich.

So sehr ich mich auf die Quelle und die Gültigkeit dieser Antwort vertraue, ich mag es ehrlich gesagt nicht, weil ich persönlich es zu kryptisch finde, also habe ich versucht, selbst eine aussagekräftigere Antwort zu finden, mit nur einem Teilerfolg. Relevant scheint § 9.4.2 / 4 zu sein:

"Es sollte genau eine Definition eines statischen Datenelements geben, das odr-used (3.2) in einem Programm ist; keine Diagnose erforderlich ist " [Hervorhebung sind meins]

Was mich ein bisschen näher an den Punkt bringt. Und so definiert § 3.2 / 2 eine odr-used Variable:

"Eine Variable, deren Name als potenziell ausgewerteter Ausdruck angezeigt wird, ist odr-used , es sei denn ist ein Objekt, das die Anforderungen für das Erscheinen in einem konstanten Ausdruck (5.19) erfüllt und die Lvalue-to-rvalue-Konvertierung (4.1) wird sofort angewendet " [Emphases are mine]

In der Frage des OP erfüllt die Variable period eindeutig die Anforderungen für das Erscheinen in einem konstanten Ausdruck, der eine Variable constexpr ist. Der Grund muss also in der zweiten Bedingung gefunden werden: " und die lvalue-to-rvalue-Konvertierung (4.1) wird sofort angewendet " .

Hier habe ich Probleme, den Standard zu interpretieren. Was bedeutet diese zweite Bedingung eigentlich? Welche Situationen werden abgedeckt? Bedeutet dies, dass eine statische Variable constexpr nicht odr-used ist (und daher in der Klasse initialisiert werden kann), wenn sie von einer Funktion zurückgegeben wird?

Allgemeiner: Was dürfen Sie mit einer statischen constexpr -Variable tun, damit Sie sie in der Klasse initialisieren können?

    
Andy Prowl 27.01.2013, 13:45
quelle

2 Antworten

3
  

Bedeutet dies, dass eine statische consExpr-Variable nicht odr-verwendet wird (und   daher kann in-class initialisiert werden) wenn es von a zurückgegeben wird   Funktion?

Ja.

Im Wesentlichen gilt: Solange Sie es als -Wert und nicht als -Objekt behandeln, wird es nicht odr-verwendet. Berücksichtigen Sie, dass der Code beim Einfügen des Werts identisch funktionieren würde, wenn er als R-Wert behandelt wird. Aber es gibt einige Szenarien, in denen das nicht der Fall wäre.

Es gibt nur wenige Szenarios, in denen die Konvertierung von lvalue-to-rvalue nicht für primitive Elemente durchgeführt wird. Dies ist die Referenzbindung, &obj und wahrscheinlich ein paar andere, aber es sind nur sehr wenige. Denken Sie daran, dass Sie, wenn der Compiler Ihnen const int& auf period verweist, die Adresse verwenden können und dass diese Adresse für jede TU gleich sein muss. Das heißt, in C ++ 's entsetzlichem TU-System, dass es eine explizite Definition geben muss.

Wenn es nicht odr-verwendet wird, kann der Compiler in jeder TU eine Kopie erstellen oder den Wert oder was auch immer er will ersetzen, und Sie können den Unterschied nicht beobachten.

    
Puppy 27.01.2013, 14:10
quelle
3

Sie haben einen Teil der Prämisse verpasst. Die oben angegebene Klassendefinition ist vollständig gültig, wenn Sie auch Account::period irgendwo definieren (aber ohne Initialisierer). Siehe die letzte sentance von 9.4.2 / 3:

  

Das Member sollte immer noch in einem Namensraumbereich definiert sein, wenn es odr-used ist   (3.2) im Programm und die Namespace-Bereichsdefinition nicht   enthält einen Initialisierer.

Diese ganze Diskussion gilt nur für statische consExpr-Datenelemente, nicht für statische Variablen im Namespace-Bereich. Wenn statische Datenelemente constexpr sind (anstatt einfach const), müssen Sie sie in der Klassendefinition initialisieren. Ihre Frage sollte also wirklich lauten: Was dürfen Sie mit einem statischen consExpr-Datenelement tun, damit Sie es im Namensraum nicht definieren müssen?

Aus dem Vorhergehenden ergibt sich die Antwort, dass das Mitglied nicht odr-used sein darf. Und Sie haben bereits relevante Teile der Definition von odr-used gefunden. So können Sie das Element in einem Kontext verwenden, in dem es nicht potenziell ausgewertet ist - als nicht evaluierter Operand (zum Beispiel von sizeof oder decltype ) oder als Unterausdruck davon. Und Sie können es in einem Ausdruck verwenden, in dem die lvalue-to-rvalue-Konvertierung sofort angewendet wird.

Jetzt sind wir also bei Welche Verwendungen einer Variablen verursachen eine sofortige Umwandlung von lvalue in rvalue?

Ein Teil dieser Antwort ist in §5 / 8:

  

Immer wenn ein glvalue-Ausdruck als Operand eines Operators erscheint   erwartet einen Prvalue für diesen Operanden, den Wert lvalue-to-rvalue (4.1),   Array-to-Pointer (4.2) oder Funktion-zu-Zeiger (4.3) Standard   Konvertierungen werden angewendet, um den Ausdruck in einen prvalue zu konvertieren.

Für arithmetische Typen, die im Wesentlichen für alle Operatoren gelten, die arithmetische Standardkonvertierungen anwenden. So können Sie das Element in verschiedenen arithmetischen und logischen Operationen verwenden, ohne eine Definition zu benötigen.

Ich kann nicht alle Dinge aufzählen, die Sie hier tun oder nicht tun können, weil die Anforderungen, dass etwas ein [g] lvalue oder [p] rval ist, über den Standard verteilt sind. Die Faustregel lautet: Wenn nur der Wert der Variablen verwendet wird, wird eine lvalue to rvalue-Konvertierung angewendet; Wenn die Variable als Objekt verwendet wird, wird sie als lvalue verwendet.

Sie können es nicht in Kontexten verwenden, in denen ein lvalue explizit benötigt wird, z. B. als Argument für den Operanden address-of oder mutierende Operatoren. Die direkte Bindung von Lvalue-Referenzen (ohne Konvertierung) ist ein solcher Kontext.

Einige weitere Beispiele (ohne detaillierte Standardanalyse):

Sie können es an Funktionen übergeben, es sei denn, der Funktionsparameter ist eine Referenz, auf die die Variable direkt gebunden werden kann.

Für die Rückgabe aus einer Funktion: Wenn die implizite Konvertierung in den Rückgabetyp eine benutzerdefinierte Konvertierungsfunktion beinhaltet, gelten die Regeln für die Übergabe an eine Funktion. Andernfalls können Sie sie zurückgeben, es sei denn, die Funktion gibt einen L-Wert (eine Referenz) zurück, der direkt auf die Variable verweist.

Die Schlüsselregel für odr-used Variablen, die "One Definition Rule" ist in 3.2 / 3:

  

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

Der "no diagnostic required" -Teil bedeutet, dass Programme, die gegen diese Regel verstoßen, zu undefiniertem Verhalten führen, das von der Kompilierung, Kompilierung und dem Fehlschlagen auf überraschende Weise zum Kompilieren und Handeln reicht, als ob alles in Ordnung wäre. Und Ihr Compiler muss Sie nicht vor dem Problem warnen.

Der Grund ist, wie andere bereits angedeutet haben, dass viele dieser Verstöße nur von einem Linker entdeckt werden. Optimierungen haben jedoch möglicherweise Verweise auf Objekte entfernt, so dass kein Grund für einen Verbindungsfehler bleibt oder das Verknüpfen manchmal nur zur Laufzeit auftritt oder definiert wird, um eine beliebige Instanz aus mehreren Definitionen eines Namens auszuwählen.

    
JoergB 27.01.2013 15:34
quelle