Имя класса, введенное внутри класса, не рассматривается как имя вложенного класса

Возьмите эти определения классов:

Определение класса 1:

struct A 
{
   struct B* m_b;
};

Определение класса 2:

struct A 
{
   struct B;
   B* m_b;
};

Оба определения класса должны объявлять B как вложенный класс. По крайней мере, это то, что я подумал, прочитав следующее из проекта стандарта С++ 11:

9.1/2 Объявление класса вводит имя класса в область, где оно объявлено, и скрывает любой класс, переменную, функцию или другое объявление этого имени в охватывающей области (3.3). Если имя класса объявляется в области, где также объявляется переменная, функция или перечислитель с тем же именем, тогда, когда оба объявления находятся в области видимости, класс может ссылаться только с использованием специфицированного спецификатора типа `

Однако g++ 4.8.2 рассматривает их по-разному. В первом определении он рассматривает B как класс, равный равному A.

Следующая программа успешно завершена:

struct A 
{
   struct B* m_b;
};

void myfun(const B& b )
{
}

int main()
{
   A a;
   myfun(*a.m_b);
}

а следующая программа:

struct A 
{
   struct B;
   B* m_b;
};

void myfun(const B& b )
{
}

int main()
{
   A a;
   myfun(*a.m_b);
}

Я понимаю, почему вторая программа не компилируется, но я не понимаю, почему первая программа успешно построена.

Я что-то пропускаю в интерпретации стандарта?

Правильно ли g++ 4.8.2 при компиляции первой программы?

Ответ 1

g++ поведение здесь совершенно правильно. Это указано в п .3.3.2 [basic.scope.pdecl]/p7 стандарта:

Точка объявления класса, сначала объявленного в специфицированный тип-спецификатор выглядит следующим образом:

  • для объявления формы
    class-key attribute-specifier-seq opt идентификатор; Идентификатор объявляется как имя класса в области, содержащей декларация, в противном случае
  • для уточненного типа-спецификатора формы идентификатор ключевого ключа, если спецификатор разработанного типа используется в предложении decl-specifier-seq или parameter-declaration-clause функция, определенная в области пространства имен, идентификатор объявляется как имя класса в пространстве имен, которое содержит объявление; в противном случае, кроме как объявление друга * идентификатор объявляется в наименьшем пространстве имен или блоке, который содержит декларация.

Обратите внимание, что во втором случае объявление всегда помещается в пространство имен или область блока, а не в область класса, поэтому он никогда не может объявить вложенный класс. Кроме того, во втором случае поиск будет выполняться, и только если ранее объявленное имя типа не будет найдено, будет создан специфицированный спецификатор типа для объявления нового имени (§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note).


* В объявлениях друзей есть свои странные правила. Имена, объявленные ранее в объявлениях друзей, по-прежнему помещаются в пространство имен (для нелокальных классов) или блока (для локальных классов), но они не становятся видимыми для большинства имен (кроме ADL в случае функций), пока они не будут также объявленные в области, содержащей их. Правила для нелокальных классов указаны в разделе 7.3.3.2 [namespace.memdef]/p3:

Если объявление друга в нелокальном классе сначала объявляет класс или функцию, класс или функция друга является членом самого внутреннего охватывающего пространства имен. Имя друга не определяется неквалифицированным поиском (3.4.1) или квалифицированным поиском (3.4.3) до тех пор, пока в этой области пространства имен не будет представлено соответствующее объявление (до или после определения класса, предоставляющего дружбу). Если вызывается функция друга, ее имя может быть найдено по имени, которое рассматривает функции из пространств имен и классов, связанных с типами аргументов функции (3.4.2). Если имя в объявлении друга не является ни квалифицированным, ни идентификатором шаблона, а декларация - это функция или специфицированный тип-спецификатор, поиск для определения того, был ли ранее объявлен объект, не должен рассматривать какие-либо области вне самого внутреннего охватывающего пространства имен.Суб >

Правила для локальных классов указаны в §11.3 [class.friend]/p11:

Если декларация друга появляется в локальном классе (9.8), и указанное имя является неквалифицированным именем, предварительное объявление просматривается без учета областей, находящихся за пределами самой внутренней охватывающей неклассической области. [...] Для объявления класса друзей, если нет предшествующего объявления, указанный класс относится к самой внутренней охватывающей неклассовой области видимости, но если впоследствии ссылаться на нее, ее имя не будет найдено по имени, объявление соответствия содержится в самой внутренней охватывающей неклассовой области.