Как компилятор разрешает бесконечные циклы ссылок?

// edited by Neil Butterworth to conserve vertical space
#include <stdio.h>

struct A;
struct B;

A& GetAInstance();
B& GetBInstance();

struct A {  
    A() {
        printf( "A\n" );
    }
    ~A() {
        printf( "~A\n" );
        B& b = GetBInstance();
    }
};

struct B {
    B() {
        printf( "B\n" );
    }

    ~B() {
        printf( "~B\n" );
        A& a = GetAInstance();
    }
}; 

A& GetAInstance() {
    static A a;
    return a;
}

B& GetBInstance() {
    static B b;
    return b;
}

int main( ) {
    A a;
}

Рассмотрим приведенный выше сценарий. Я ожидаю, что это приведет к бесконечному эталонному циклу, в результате чего программа не сможет выйти из статической деинициализации, но программа отлично справилась со следующей распечаткой:

  A
  ~A
  B
  ~B
  A
  ~A

Это было неожиданно.

Как компилятор справляется с этой ситуацией? Какие алгоритмы он использует для решения бесконечной рекурсии? Или я неправильно понял что-то фундаментальное? Это где-то в стандарте, определяемом как undefined?

Ответ 1

Компилятор эффективно сохраняет bool с каждым static, чтобы запомнить, был ли он инициализирован.

Это порядок:

Внутри main:

  • Построить A
  • Уничтожить A
    • Построить static B

Очистка статики:

  • Уничтожить static B
    • Построить static A
    • Уничтожить static A

3.6.3/1 в стандарте указывает, что он должен работать таким образом, даже когда a static создается при очистке, как в этом случае.

Ответ 2

Это поведение, вероятно, вызвано тем, что вы используете статические экземпляры. Компилятор наверняка заботится о статусе статических переменных, чтобы избежать выполнения нескольких инициализаций/деинициализаций.