Метки заставляют объекты создаваться и разрушаться?

Почему эта программа выводит aAaA, а не aaAA или aA?

#include <iostream>

struct A {
  A() { std::cout << "a"; }
  ~A() { std::cout << "A"; }
};

int i;

int main() {
a:A a;
  if (i-->=0) goto a;
}

Ответы (3 шт):

Автор решения: Harry

А что не так?

Зашли. Создался объект. Проверились - да, i>=0, пошли к метке a. Объект выходит из области видимости и уничтожается.

Создается новый. Проверка - i<0 - все, идем дальше. Окончание программы, объект выходит из области видимости, уничтожается...

aAaA

Что не так? :)

→ Ссылка
Автор решения: AR Hovsepyan

Ну да, это то же самое, что написать:

do {
    A a;
} while (i-- >= 0);

объект локальный, и он каждый раз создается и уничтожается

→ Ссылка
Автор решения: Qwertiy

Добавим побольше точек вывода: https://ideone.com/C97AxT

#include <iostream>

using namespace std;

struct A
{
  A() { cout << '+'; }
  ~A() { cout << '-'; }
};

int i;

int main()
{
  cout << '^';

a:
  cout << '0';
  A a;
  cout << '1';

  if (i-- >= 0) {
    cout << 'g';
    goto a;
  }

  cout << '$';
}

Получается:

^0+1g-0+1$-

Видим два создания объекта и 2 уничтожения. Уничтожения происходят в конце жизни объекта, который наступает сразу после goto и сразу после конца функции. Таким образом само по себе наличие goto не влияет на область жизни, влияет именно его исполнение.

Почему так делается, я точно не знаю, но у меня есть предположение. Компилятор обязан обеспечить корректность вызова конструкторов и деструкторов, а переход через goto в данном случае завершает блок - никакой другой возможности уничтожить переменную не будет. Теоретически, можно было бы пытаться уничтожить её после g0, но между g и 0 обратиться к ней затруднительно. Во-первых, очевидно, что в пару к goto поставить деструктор проще, во-вторых в принципе это выглядит логичнее - как конец блока по аналогии с break или continue, а не как неизвестное место перед вызовом конструктора (а если ещё один goto и он вообще не вызовется?). Два значения одна переменная хранить не может.

Почему я акцентировал влияние на данном случае? Потому что компилятор понимает, что переход делается выше переменной. Если мы немного переставим строки и поднимем объявление переменной выше метки (https://ideone.com/ZLK6xb), то она не будет уничтожена:

^+01g01$-
→ Ссылка