Czy przeskakiwanie zmiennej inicjalizacji jest źle sformułowane lub powoduje niezdefiniowane zachowanie?

17

Rozważ ten kod:

void foo()
{
    goto bar;
    int x = 0;
    bar: ;
}

GCC i Clang odrzucają to , ponieważ przejście do bar:obejścia inicjalizacji zmiennej. MSVC nie narzekają na wszystko (z wyjątkiem stosowania xpo bar:powoduje ostrzeżenie).

Możemy zrobić podobnie z switch:

void foo()
{
    switch (0)
    {
        int x = 0;
        case 0: ;
    }
}

Teraz wszystkie trzy kompilatory emitują błędy .

Czy te fragmenty są źle sformułowane? A może powodują UB?

Kiedyś myślałem, że oba są źle sformułowane, ale nie mogę znaleźć rewelacyjnych części standardu. [stmt.goto] nic o tym nie mówi, podobnie jak [stmt.select] .

HolyBlackCat
źródło
1
Problem byłby bardziej trywialny, jeśli użyjesz go xpo skoku.
Jarod42
1
nie jest to standard, ale tutaj można znaleźć informacje na ten temat: w szczególności en.cppreference.com/w/cpp/language/goto : „Jeżeli przeniesienie kontroli wchodzi w zakres dowolnych zmiennych automatycznych (np. przeskakując do przodu deklaracji instrukcja), program jest źle sformułowany (nie można go skompilować), chyba że ... ”
idclev 463035818
Dodaj /permissive-flagę do MSVC, a ona również narzeka. Nie wiem jednak, czy zachowanie MSVC bez tej flagi jest dobrze zdefiniowane (zakładam, że tak, w przeciwnym razie dlaczego mieliby na to pozwolić?).
orzech
@walnut „w przeciwnym razie dlaczego mieliby na to pozwolić” Prawdopodobnie ze względu na wsteczną kompatybilność lub dlatego, że nie przejmują się zbytnio standardem. Wszystkie główne kompilatory nie są zgodne ze standardem w ustawieniach domyślnych.
HolyBlackCat

Odpowiedzi:

20

Jest źle sformułowany, gdy inicjalizacja nie jest pusta.

[stmt.dcl]

3 Możliwe jest przeniesienie do bloku, ale nie w sposób, który omija deklaracje przy inicjalizacji (w tym deklaracje warunków i instrukcje init). Program, który przeskakuje z miejsca, w którym zmienna z automatycznym czasem przechowywania nie wchodzi w zakres, do punktu, w którym jest objęty zakresem, jest źle sformułowany, chyba że zmienna ma próżną inicjalizację ([podstawowe. życie]). W takim przypadku zmienne z próżną inicjalizacją są konstruowane w kolejności ich deklaracji.

Inicjator sprawia, że ​​inicjalizacja nie jest pusta. Dla kontrastu to

void foo()
{
    goto bar;
    int x; // no initializer
    bar: ;
}

byłby dobrze uformowany. Miałyby jednak zastosowanie zwykłe zastrzeżenia dotyczące używania xz nieokreśloną wartością.

StoryTeller - Unslander Monica
źródło
czy deklaracje zmiennych nie muszą być pierwszą rzeczą w zakresie?
Cruncher
4
@Cruncher - C89 tego wymagał. C ++ nigdy tak nie było, podobnie jak współczesne C.
StoryTeller - Unslander Monica
3

Z oświadczenia goto :

Jeśli przekazanie kontroli wchodzi w zakres dowolnych zmiennych automatycznych (np. Przeskakując do przodu w instrukcji deklaracji), program jest źle sformułowany (nie można go skompilować), chyba że wszystkie zmienne, których zakres został wprowadzony, mają

  1. typy skalarne zadeklarowane bez inicjatorów
  2. typy klas z trywialnymi domyślnymi konstruktorami i trywialnymi destruktorami zadeklarowanymi bez inicjatorów
  3. wersje jednego z powyższych zakwalifikowanych do CV
  4. tablice jednego z powyższych
Poszukiwacz prawdy
źródło