[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:
źródło
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ślix
jest typuint
, została zainicjowana domyślnie i nie została wcześniej przypisana). Nie można łączyć tych dwóch w jedną.Odpowiedzi:
Zostało to otwarte jako wydanie redakcyjne . Został przekazany do CWG w celu (wewnętrznej) dyskusji. Około 24 godzin później osoba, która przesłała problem, utworzyła żądanie ściągnięcia, które modyfikuje przykład, aby wyjaśnić, że jest to UB:
Ten PR został dodany i problem został zamknięty. Wydaje się więc jasne, że oczywista interpretacja (UB ze względu na dostęp do obiektu, którego żywotność jeszcze się nie rozpoczęła) jest interpretacją zamierzoną. Wydaje się, że intencją komitetu jest sprawienie, aby konstrukty te nie działały, a nienormatywny tekst normy został zaktualizowany, aby to odzwierciedlić.
źródło