Zastosowanie zmiennej we własnym inicjatorze

22

[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ż xwartość 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 charzmiennej z wartości unsigned charo 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::bytenp 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 xwartość 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 xwartość jest używana przed xzakoń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ż xmiał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ść xw 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:

orzech włoski
źródło
@LanguageLawyer Nie jestem pewien, czy mam rację, szczególnie jeśli nikt jeszcze nie odpowiedział. Jeśli inni zamierzają się ze mną zgodzić tutaj, mogę złożyć wniosek później (a może ktoś inny przede mną), ale nie chcę zgłaszać problemów, których nie jestem pewien.
orzech
@LanguageLawyer: Nie może być problemu redakcyjnego, jeśli dokument roboczy jednoznacznie mówi niewłaściwą rzecz.
Davis Herring,
1
Słowo zostało zmienione przez P1358 .
xskxzr
1
@xskxzr Racja, a tymczasem LanguageLawyer złożył również kwestię redakcyjną , która wydaje się być przesłana do CWG w celu wyjaśnienia intencji.
orzech
1
@ clockw0rk 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 xjest typu int, została zainicjowana domyślnie i nie została wcześniej przypisana). Nie można łączyć tych dwóch w jedną.
orzech

Odpowiedzi:

8

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:

Tutaj inicjalizacja drugiego \ tcode {x} ma niezdefiniowane zachowanie, ponieważ inicjator uzyskuje dostęp do drugiego \ tcode {x} poza okresem jego ważności \ iref {basic.life}.

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ć.

Nicol Bolas
źródło