类内部引入的类名不被视为嵌套类名
采用这些类定义:
类定义1:
struct A
{
struct B* m_b;
};
类定义2:
struct A
{
struct B;
B* m_b;
};
两个类定义都应将 B
声明为嵌套类.至少,通过阅读 C++11 标准草案中的以下内容,我是这么认为的:
Both the class defintions should declare B
as a nested class. At least, that's what I thought by reading the following from the draft C++11 standard:
9.1/2 类声明将类名引入到声明它的作用域中,并将该名称的任何类、变量、函数或其他声明隐藏在封闭作用域中(3.3).如果在一个范围内声明了一个类名,其中还声明了一个同名的变量、函数或枚举器,那么当两个声明都在范围内时,只能使用 elaborated-type-specifier 来引用该类 `
9.1/2 A class declaration introduces the class name into the scope where it is declared and hides any class, variable, function, or other declaration of that name in an enclosing scope (3.3). If a class name is declared in a scope where a variable, function, or enumerator of the same name is also declared, then when both declarations are in scope, the class can be referred to only using an elaborated-type-specifier `
但是,g++ 4.8.2 对它们的处理方式不同.在第一个定义中,它将 B
视为与 A
对等的类.
However, g++ 4.8.2 treats them differently. In the first definition, it treats B
as a class that is a peer to 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);
}
我明白为什么第二个程序没有编译,但我不明白为什么第一个程序编译成功.
I understand why the second program does not compile but I don't understand why the first program is built successfully.
我是否在解释标准时遗漏了什么?
Am I missing something in the interpretation of the standard?
g++ 4.8.2 编译第一个程序是否正确?
Is g++ 4.8.2 correct in compiling the first program?
推荐答案
g++ 在这里的行为是完全正确的.这在标准的 §3.3.2 [basic.scope.pdecl]/p7 中有规定:
g++'s behavior here is entirely correct. This is specified in §3.3.2 [basic.scope.pdecl]/p7 of the standard:
类的声明点首先在一个详细类型说明符如下:
The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
- 对于表单的声明
class-key attribute-specifier-seqopt identifier ;
标识符被声明为一个 class-name 在包含声明,否则 - 对于
形式的 详细类型说明符class-key identifier
如果 elaborated-type-specifier 用于 decl-specifier-seq 或 parameter-a 的声明子句在命名空间范围内定义的函数,标识符被声明为class-name 在包含声明的命名空间中;否则,除了作为友元声明*,标识符被声明在包含声明.
- for a declaration of the form
class-key attribute-specifier-seqopt identifier ;
the identifier is declared to be a class-name in the scope that contains the declaration, otherwise - for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a function defined in namespace scope, the identifier is declared as a class-name in the namespace that contains the declaration; otherwise, except as a friend declaration*, the identifier is declared in the smallest namespace or block scope that contains the declaration.
请注意,在第二种情况下,声明总是放在命名空间或块范围内,而不是类范围内,因此它永远不能声明嵌套类.此外,在第二种情况下,将执行查找,并且仅当未找到先前声明的 type-name 时,才会采用 elaborated-type-specifier 来声明新名称(§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note).
Note that in the second case the declaration is always placed in a namespace or block scope, never a class scope, so it can never declare a nested class. Also, in the second case, a lookup will be performed and only if a previously declared type-name is not found will the elaborated-type-specifier be taken to declare a new name (§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note).
* Friend 声明有自己奇怪的规则.在友元声明中首先声明的名称仍然放置在命名空间(对于非本地类)或块(对于本地类)范围内,但是对于大多数名称查找来说它们是不可见的(在函数的情况下 ADL 除外),直到它们也在包含它们的范围内声明.非本地类的规则在 §7.3.1.2 [namespace.memdef]/p3:
* Friend declarations have their own weird rules. The names first declared in friend declarations are still placed in namespace (for non-local classes) or block (for local classes) scope, but they are not made visible for most name lookup (except for ADL in case of functions) until they are also declared in the scope containing them. The rules for non-local classes are specified in §7.3.1.2 [namespace.memdef]/p3:
如果非本地类中的友元声明首先声明了一个类或函数,则友元类或函数是最内层封闭命名空间的成员.在该命名空间范围内(在授予友谊的类定义之前或之后)提供匹配声明之前,未限定查找 (3.4.1) 或限定查找 (3.4.3) 无法找到朋友的名称.如果调用友元函数,则可以通过名称查找找到其名称,该名称查找考虑来自与函数参数类型关联的命名空间和类的函数(3.4.2).如果朋友声明中的名称既不是限定的也不是 template-id 并且声明是一个函数或一个 elaborated-type-specifier,查找以确定实体是否已经先前声明的不应考虑最内层封闭命名空间之外的任何范围.
If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.
本地类的规则在 §11.3 [class.friend]/p11: 中指定:
如果友元声明出现在本地类 (9.8) 中并且指定的名称是非限定名称,则在不考虑最内层非类范围之外的范围的情况下查找先前声明.[...] 对于友元类声明,如果没有事先声明,则指定的类属于最内层的非类范围,但如果随后引用它,则名称查找不会找到它的名称,直到匹配的声明在最里面的非类范围内提供.
相关文章