在类中引入的类名不被视为嵌套类名

R S*_*ahu 13 c++ g++ c++11

采取这些类定义:

班级定义1:

struct A 
{
   struct B* m_b;
};
Run Code Online (Sandbox Code Playgroud)

班级定义2:

struct A 
{
   struct B;
   B* m_b;
};
Run Code Online (Sandbox Code Playgroud)

类定义都应该声明B为嵌套类.至少,通过阅读C++ 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);
}
Run Code Online (Sandbox Code Playgroud)

而以下程序没有:

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

void myfun(const B& b )
{
}

int main()
{
   A a;
   myfun(*a.m_b);
}
Run Code Online (Sandbox Code Playgroud)

我理解为什么第二个程序没有编译,但我不明白为什么第一个程序是成功构建的.

我在标准的解释中遗漏了什么吗?

编译第一个程序时g ++ 4.8.2是否正确?

T.C*_*.C. 16

g ++在这里的行为是完全正确的.这在标准的§3.3.2[basic.scope.pdecl]/p7中指定:

详细说明的类型说明符中首先声明的类的声明点 如下:

  • 用于声明
    class-key attribute-specifier-seq opt identifier的形式;
    标识符在包含声明的作用域中声明为类名,否则
  • 如果在命名空间作用域中定义的函数的decl-specifier-seqparameter-declaration-clause中使用了elaborated-type-specifier,那么对于表
    类键标识符
    详细类型说明符,标识符被声明为 包含声明的命名空间中的class-name ; 否则,除了作为朋友声明*之外,标识符在包含声明的最小名称空间或块作用域中声明.

请注意,在第二种情况下,声明始终放在命名空间或块作用域中,而不是类作用域,因此它永远不能声明嵌套类.此外,在第二种情况下,将执行查找,并且只有在未找到先前声明的类型名称时才会使用详细类型说明符来声明新名称(§3.4.4[basic.lookup.elab]/p2,§9.1[class.name]/p3 note).


*朋友声明有他们自己奇怪的规则.在友元声明中首先声明的名称仍然放在命名空间(对于非本地类)或块(对于本地类)作用域中,但是对于大多数名称查找(除了函数的ADL除外)它们都不可见,直到它们为止.也在包含它们的范围内声明.非本地类的规则在§7.3.1.2[namespace.memdef]/p3中指定:

如果非本地类中的友元声明首先声明一个类或函数,那么友元类或函数是最内层封闭命名空间的成员.在非命名查找(3.4.1)或限定查找(3.4.3)之前找不到朋友的名称,直到在该命名空间范围内提供匹配声明(在授予友谊的类定义之前或之后).如果调用了友元函数,则可以通过名称查找找到其名称,该名称查找考虑名称空间中的函数和与函数参数类型相关联的类(3.4.2).如果朋友声明中的名称既不是合格的也不是模板ID,并且声明是函数或详细类型说明符,确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何范围.

本地类的规则在§11.3[class.friend]/p11中指定:

如果友元声明出现在本地类(9.8)中并且指定的名称是非限定名称,则会查找先前声明,而不考虑最内部封闭非类作用域之外的作用域.[...]对于友元类声明,如果没有先前的声明,则指定的类属于最内层的非类作用域,但如果随后引用它,则名称查找时找不到其名称,直到匹配声明在最内层的非类范围内提供.