Круговая зависимость С++ - пространство имен vs struct

Пожалуйста, просветите меня. Почему это компилируется:

struct compiles
{
    struct A;
    struct B
    {
        B(const A &a) : member(a.member) { }
        int member;
    };
    struct A
    {
        A(const B &b) : member(b.member) { }
        int member;
    };
};

пока это не так:

namespace doesnt
{
    struct A;
    struct B
    {
        B(const A &a) : member(a.member) { }
        int member;
    };
    struct A
    {
        A(const B &b) : member(b.member) { }
        int member;
    };
}

(в MSVC 9.0)

Ответы

Ответ 1

В С++ класс scope является специальным. Любое объявление, которое продолжается до или после конца определения класса, автоматически распространяется на регионы, определенные его определениями (3.3.6 [basic.scope.class]).

Это означает, что в первом случае как первое объявление struct A, так и полное определение struct A видны в теле B и его конструкторе.

Это не относится к области пространства имен, поэтому во втором случае a.member в конструкторе B является ошибкой, потому что определение struct A еще не видно.

Ответ 2

Тело определения class/struct/union обрабатывается сразу, что позволяет ссылаться на членов классов, определенных позже. A namespace обрабатывается сверху вниз, а форвардное объявление struct A не позволяет использовать его элементы без определения. Попробуйте переместить определение конструктора B вне класса, чтобы вы могли поместить его после определения A.

Ответ 3

[Также проверено с помощью g++ 4.2] Первый компилируется, потому что компилятор полностью отображает все типы, определенные в структуре, прежде чем компилировать вложенные структуры (подумайте о публичных встроенных методах, используя частные атрибуты, которые появляются позже в классе). В пространстве имен компилятор просто работает сверху вниз и не имеет специальных правил.

Ответ 4

оба будут компилироваться, если вы переместите реализации конструкторов в файл .cpp, где они должны быть в любом случае.