[basic.scope.pdecl] / 1 standardowego projektu C ++ 20 zawierał następujący (nienormatywny) przykład w notatce (częściowy cytat sprzed scalenia żądania ściągnięcia 3580 , patrz odpowiedź na to pytanie):
unsigned char x = x;
[...] x jest inicjalizowany z własną (nieokreśloną) wartością.
Czy to rzeczywiście ma dobrze zdefiniowane zachowanie w C ++ 20?
Generalnie samoinicjalizacja formularza T x = x;
ma niezdefiniowane zachowanie, ponieważ x
wartość jest nieokreślona przed zakończeniem inicjalizacji. Szacowanie nieokreślonych wartości generalnie powoduje niezdefiniowane zachowanie ( [basic.indent] / 2 ), ale istnieje szczególny wyjątek w [basic.indent] /2.3, który pozwala na bezpośrednią inicjalizację unsigned char
zmiennej z wartości unsigned char
o nieokreślonej wartości (powodując inicjalizację z nieokreśloną wartością ).
To samo nie powoduje zatem nieokreślonego zachowania, ale dotyczyłoby innych typów T
, które nie są niepodpisanymi wąskimi typami znaków lub std::byte
np int x = x;
. Te rozważania zastosowane w C ++ 17 i wcześniej, patrz także powiązane pytania na dole.
Jednak nawet w unsigned char x = x;
przypadku obecnej wersji roboczej [basic.lifetime] / 7 mówi:
Podobnie przed rozpoczęciem życia obiektu [...] korzystanie z właściwości glvalue, które nie zależą od jego wartości, jest dobrze zdefiniowane. Program ma niezdefiniowane zachowanie, jeśli:
glvalue służy do uzyskania dostępu do obiektu, lub
[...]
Wydaje się to sugerować, że x
wartość w tym przykładzie może być wykorzystana tylko w okresie jej istnienia.
[basic.lifetime] / 1 mówi:
[...]
Żywotność obiektu typu T rozpoczyna się, gdy:
- [...] i
- jego inicjalizacja (jeśli istnieje) jest zakończona (w tym nieudana inicjalizacja) ([dcl.init]),
[...]
Tak więc x
życie zaczyna się dopiero po zakończeniu inicjalizacji. Ale w przytoczonym przykładzie x
wartość jest używana przed x
zakończeniem inicjalizacji. Dlatego użycie ma nieokreślone zachowanie.
Czy moja analiza jest poprawna i jeśli tak, to czy wpływa na podobne przypadki użycia przed inicjalizacją, takie jak
int x = (x = 1);
które, o ile wiem, były dobrze zdefiniowane w C ++ 17 i wcześniej?
Zauważ, że w C ++ 17 (wersja ostateczna) drugi wymóg rozpoczęcia życia był inny :
- jeśli obiekt ma niewymuszoną inicjalizację, inicjalizacja jest zakończona,
Ponieważ x
miałoby to próżną inicjalizację według definicji C ++ 17 (ale nie tej w bieżącym projekcie), jego żywotność zacząłaby się już, gdy jest dostępna w inicjalizatorze w przykładach podanych powyżej, a więc w obu przykładach nie było niezdefiniowanego zachowania ze względu na żywotność x
w C ++ 17.
Sformułowanie przed C ++ 17 jest znowu inne, ale z takim samym skutkiem.
Pytanie nie dotyczy nieokreślonego zachowania podczas korzystania z nieokreślonych wartości, które zostały uwzględnione np. W następujących pytaniach:
int x ^= x;
nie jest poprawnie sformatowany . Możesz mieć definicję zmiennej z inicjatorem (tj. int x = x;
Chociaż jest to UB) lub instrukcję wyrażenia przypisania xor (tj. x ^= x;
Chociaż jest to UB, jeśli x
jest typu int
, została zainicjowana domyślnie i nie została wcześniej przypisana). Nie można łączyć tych dwóch w jedną.