类内部引入的类名不被视为嵌套类名

Posted

技术标签:

【中文标题】类内部引入的类名不被视为嵌套类名【英文标题】:A class name introduced inside a class is not treated as a nested class name 【发布时间】:2014-08-16 21:22:41 【问题描述】:

采用这些类定义:

类定义1:

struct A 

   struct B* m_b;
;

类定义2:

struct A 

   struct B;
   B* m_b;
;

两个类定义都应将B 声明为嵌套类。至少,通过阅读 C++11 标准草案中的以下内容,我是这么认为的:

9.1/2 类声明将类名引入到声明它的作用域中,并将该名称的任何类、变量、函数或其他声明隐藏在封闭作用域中(3.3)。如果在一个范围内声明了一个类名,其中还声明了一个同名的变量、函数或枚举器,那么当两个声明都在范围内时,只能使用 elaborated-type-specifier 来引用该类 `

但是,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-seqopt 标识符的声明; 标识符被声明为 类名 在包含的范围内 声明,否则 对于 class-key identifier 形式的 elaborated-type-specifier 如果 elaborated-type-specifier 用于 decl-specifier-seqparameter-declaration-clause 在命名空间范围内定义的函数,标识符被声明为 class-name 在包含声明的命名空间中;否则,除了作为友元声明*,标识符被声明 在包含 声明。

请注意,在第二种情况下,声明总是放在命名空间或块范围内,而不是类范围内,因此它永远不能声明嵌套类。此外,在第二种情况下,将执行查找,并且仅当未找到先前声明的 type-name 时,才会采用 elaborated-type-specifier 来声明新名称(§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note)。


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

如果非本地类中的友元声明首先声明了一个类或函数,则友元类或函数是最内层封闭命名空间的成员。在该命名空间范围内(在授予友谊的类定义之前或之后)提供匹配声明之前,未限定查找 (3.4.1) 或限定查找 (3.4.3) 无法找到朋友的名称。如果调用友元函数,则可以通过名称查找找到其名称,该名称查找考虑来自与函数参数类型相关联的命名空间和类的函数(3.4.2)。如果 朋友声明中的名称既不是限定的也不是 template-id 并且声明是一个函数或一个 elaborated-type-specifier,查找以确定实体是否已经先前声明的不应考虑最内层封闭命名空间之外的任何范围。

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

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

【讨论】:

你能更详细地解释一下你上面写的“注意在第二种情况下......”吗?我的理解是这条注释适用于 first 代码,标识符B 被认为是在包含声明的最小命名空间或块范围内声明的类名,即全局命名空间.那是对的吗?如果是这样,这背后的原因是什么? @WakeupBrazil “第二种情况”,如标准引用中的第二个要点,而不是问题中的第二段代码。

以上是关于类内部引入的类名不被视为嵌套类名的主要内容,如果未能解决你的问题,请参考以下文章

如何使用嵌套的类名? [复制]

java面试中的简答题(含答案)

如何在css模块中嵌套类并做出反应?

通过添加到 SASS 中的类名来嵌套 css 选择器

Java类名与嵌套包名相同

内部类