Verwendung einer Variablen im eigenen Initialisierer

22

[basic.scope.pdecl] / 1 des C ++ 20-Standardentwurfs enthielt das folgende (nicht normative) Beispiel in einer Notiz (Teilzitat vor dem Zusammenführen der Pull-Anforderung 3580 , siehe Antwort auf diese Frage):

unsigned char x = x;

[...] x wird mit einem eigenen (unbestimmten) Wert initialisiert.

Hat dies tatsächlich ein genau definiertes Verhalten in C ++ 20?


Im Allgemeinen hat die Selbstinitialisierung des Formulars T x = x;ein undefiniertes Verhalten, da der xWert vor Abschluss der Initialisierung unbestimmt ist. Das Auswerten unbestimmter Werte führt im Allgemeinen zu undefiniertem Verhalten ( [basic.indent] / 2 ). In [basic.indent] /2.3 gibt es jedoch eine spezielle Ausnahme , die das direkte Initialisieren einer unsigned charVariablen aus einem l- unsigned charWert mit unbestimmtem Wert ermöglicht (was das Initialisieren mit einem unbestimmten Wert verursacht) ).

Dies allein verursacht daher kein undefiniertes Verhalten, sondern würde für andere Typen T, die keine vorzeichenlosen engen Zeichentypen sind, oder std::bytez int x = x;. Diese Überlegungen wurden auch in C ++ 17 und früher angewendet, siehe auch verknüpfte Fragen unten.

Selbst für unsigned char x = x;den aktuellen Entwurf [basic.lifetime] / 7 heißt es jedoch:

In ähnlicher Weise ist die Verwendung der Eigenschaften des Gl-Werts, die nicht von seinem Wert abhängen, genau definiert, bevor die Lebensdauer eines Objekts [...] begonnen hat. Das Programm hat ein undefiniertes Verhalten, wenn:

  • Der gl-Wert wird verwendet, um auf das Objekt zuzugreifen, oder

  • [...]

Dies scheint zu implizieren, dass xder Wert im Beispiel nur während seiner Lebensdauer verwendet werden kann.

[basic.lifetime] / 1 sagt:

[...]

Die Lebensdauer eines Objekts vom Typ T beginnt, wenn:

  • [...] und
  • seine Initialisierung (falls vorhanden) ist abgeschlossen (einschließlich leerer Initialisierung) ([dcl.init]),

[...]

Somit xbeginnt die Lebensdauer erst, nachdem die Initialisierung abgeschlossen ist. Im angegebenen Beispiel wird xder Wert jedoch verwendet, bevor xdie Initialisierung abgeschlossen ist. Daher hat die Verwendung undefiniertes Verhalten.

Ist meine Analyse korrekt und wenn ja, hat sie Auswirkungen auf ähnliche Fälle der Verwendung vor der Initialisierung wie z

int x = (x = 1);

Welche waren, soweit ich das beurteilen kann, auch in C ++ 17 und früher gut definiert?


Beachten Sie, dass in C ++ 17 (endgültiger Entwurf) die zweite Voraussetzung für den Beginn der Lebensdauer anders war :

  • Wenn das Objekt eine nicht leere Initialisierung hat, ist seine Initialisierung abgeschlossen.

Da xdie Initialisierung nach der Definition von C ++ 17 (aber nicht nach der Definition im aktuellen Entwurf) leer wäre, hätte ihre Lebensdauer bereits begonnen, wenn im Initialisierer in den oben angegebenen Beispielen darauf zugegriffen wird, und daher gab es in beiden Beispielen kein undefiniertes Verhalten aufgrund der Lebensdauer von xin C ++ 17.

Der Wortlaut vor C ++ 17 ist wieder anders, aber mit dem gleichen Ergebnis.


Bei der Frage geht es nicht um undefiniertes Verhalten bei Verwendung unbestimmter Werte, das z. B. in folgenden Fragen behandelt wurde:

Nussbaum
quelle
@LanguageLawyer Ich bin nicht sicher, ob ich richtig bin, besonders nicht, wenn noch niemand geantwortet hat. Wenn andere mir hier zustimmen, kann ich später eine einreichen (oder vielleicht wird es jemand anderes vor mir tun), aber ich möchte keine Probleme einreichen, bei denen ich mir nicht sicher bin.
Walnuss
@LanguageLawyer: Es kann kein redaktionelles Problem sein, wenn das Arbeitspapier eindeutig das Falsche sagt.
Davis Herring
1
Das Wort wird durch P1358 geändert .
xskxzr
1
@xskxzr Richtig, und in der Zwischenzeit hat LanguageLawyer auch eine redaktionelle Ausgabe eingereicht , die zur Klärung der Absicht an CWG weitergeleitet worden zu sein scheint.
Walnuss
1
@ clockw0rk int x ^= x;ist syntaktisch nicht wohlgeformt. Sie können entweder eine Variablendefinition mit dem Initialisierer (dh int x = x;obwohl es sich um UB handelt) oder eine xor-Zuweisungsausdrucksanweisung (dh x ^= x;obwohl es sich um UB xhandelt , wenn es sich um einen Typ handelt int, die standardmäßig initialisiert und nicht zuvor zugewiesen wurde) haben. Sie können diese beiden nicht zu einem mischen.
Walnuss

Antworten:

8

Dies wurde als eröffnet redaktionelle Ausgabe eröffnet . Es wurde zur (internen) Diskussion an CWG weitergeleitet. Ungefähr 24 Stunden später erstellte die Person, die das Problem weitergeleitet hat, eine Pull-Anforderung, mit der das Beispiel geändert wird, um zu verdeutlichen, dass es sich um UB handelt:

Hier hat die Initialisierung des zweiten \ tcodes {x} ein undefiniertes Verhalten, da der Initialisierer außerhalb seiner Lebensdauer \ iref {basic.life} auf den zweiten \ tcode {x} zugreift.

Diese PR wurde inzwischen hinzugefügt und das Problem geschlossen. Es scheint also klar zu sein, dass die offensichtliche Interpretation (UB aufgrund des Zugriffs auf ein Objekt, dessen Lebensdauer noch nicht begonnen hat) die beabsichtigte Interpretation ist. Es scheint, dass die Absicht des Ausschusses darin besteht , diese Konstrukte funktionsunfähig zu machen, und der nicht normative Text des Standards wurde aktualisiert, um dies widerzuspiegeln.

Nicol Bolas
quelle