限定名查找
限定 名是出现在作用域解析运算符 ::
右侧的名称 (另见 限定标识符)。 限定名可以指代
- 类成员 (包括静态和非静态函数、类型、模板等),
- 命名空间成员 (包括另一个命名空间),
- 枚举器。
如果 ::
的左侧没有任何内容,则查找仅考虑 全局命名空间作用域 中的声明。 这使得即使名称被局部声明隐藏,也可以引用这些名称
#include <iostream> namespace M { const char* fail = "fail\n"; } using M::fail; namespace N { const char* ok = "ok\n"; } using namespace N; int main() { struct std {}; std::cout << ::fail; // Error: unqualified lookup for 'std' finds the struct ::std::cout << ::ok; // OK: ::std finds the namespace std }
在对 ::
右侧的名称执行名称查找之前,必须完成对其左侧名称的查找 (除非使用了 decltype 表达式,或者左侧没有任何内容)。 此查找 (可能是限定的或非限定的,取决于该名称的左侧是否还有另一个 ::
) 仅考虑命名空间、类类型、枚举和特化为类型的模板。 如果在左侧找到的名称未指定命名空间或类、枚举或依赖类型,则程序是非良构的
struct A { static int n; }; int main() { int A; A::n = 42; // OK: unqualified lookup of A to the left of :: ignores the variable A b; // Error: unqualified lookup of A finds the variable A } template<int> struct B : A {}; namespace N { template<int> void B(); int f() { return B<0>::n; // Error: N::B<0> is not a type } }
当限定名用作 声明符 时,则在成员的类或命名空间的作用域中执行在同一声明符中,位于该限定名之后 (但不位于其之前) 的名称的 非限定查找
class X {}; constexpr int number = 100; struct C { class X {}; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // Error: look up for X finds ::X, not C::X C::X C::arr[number], brr[number]; // OK: size of arr is 50, size of brr is 100
如果 ::
后跟字符 ~
,然后是标识符 (即,它指定析构函数或伪析构函数),则在该标识符的查找作用域与 ::
左侧名称的作用域相同
struct C { typedef int I; }; typedef int I1, I2; extern int *p, *q; struct A { ~A(); }; typedef A AB; int main() { p->C::I::~I(); // The name I after ~ is looked up in the same scope as I before :: // (that is, within the scope of C, so it finds C::I) q->I1::~I2(); // The name I2 is looked up in the same scope as I1 // (that is, from the current scope, so it finds ::I2) AB x; x.AB::~AB(); // The name AB after ~ is looked up in the same scope as AB before :: // (that is, from the current scope, so it finds ::AB) }
枚举器如果左侧名称的查找结果是 枚举 (作用域限定或非作用域限定),则右侧的查找结果必须是属于该枚举的枚举器,否则程序是非良构的。 |
(自 C++11 起) |
[编辑] 类成员
如果左侧名称的查找结果是类/结构体或联合体名称,则在 ::
右侧的名称在该类的作用域中查找 (因此可能会找到该类的成员或其基类的声明),但有以下例外
- 析构函数的查找如上所述 (在
::
左侧名称的作用域中)。 - 在 用户定义的转换 函数名称中的 conversion-type-id 首先在该类的作用域中查找。 如果找不到,则在当前作用域中查找该名称。
- 模板参数中使用的名称在当前作用域中查找 (而不是在模板名称的作用域中)。
- using 声明 中的名称也考虑被在同一作用域中声明的变量、数据成员、函数或枚举器的名称隐藏的类/枚举名称。
本节尚不完整 原因:上述内容的微示例 |
如果 ::
的右侧与左侧命名相同的类,则该名称指定该类的构造函数。 这种限定名只能用于构造函数的声明和 using 声明 中,用于继承构造函数。 在忽略函数名称的查找中 (即,当查找 ::
左侧的名称时,当在 详述类型说明符 或 基类说明符 中查找名称时),相同的语法解析为注入类名称
struct A { A(); }; struct B : A { B(); }; A::A() {} // A::A names a constructor, used in a declaration B::B() {} // B::B names a constructor, used in a declaration B::A ba; // B::A names the type A (looked up in the scope of B) A::A a; // Error: A::A does not name a type struct A::A a2; // OK: lookup in elaborated type specifier ignores functions // so A::A simply names the class A as seen from within the scope of A // (that is, the injected-class-name)
限定名查找可用于访问被嵌套声明或派生类隐藏的类成员。 对限定成员函数的调用永远不是虚函数
struct B { virtual void foo(); }; struct D : B { void foo() override; }; int main() { D x; B& b = x; b.foo(); // Calls D::foo (virtual dispatch) b.B::foo(); // Calls B::foo (static dispatch) }
[编辑] 命名空间成员
如果 ::
左侧的名称引用命名空间,或者 ::
左侧没有任何内容 (在这种情况下,它引用全局命名空间),则在 ::
右侧出现的名称在该命名空间的作用域中查找,但有以下例外
- 模板参数中使用的名称在当前作用域中查找
namespace N { template<typename T> struct foo {}; struct X {}; } N::foo<X> x; // Error: X is looked up as ::X, not as N::X
在 命名空间 N
的作用域内进行限定查找时,首先考虑位于 N
中的所有声明以及位于 N
的 内联命名空间成员 中的所有声明 (以及传递地,在其内联命名空间成员中)。 如果该集合中没有声明,则它会考虑在 N
中找到的 using 指令 以及 N
的所有传递内联命名空间成员命名的所有命名空间中的声明。 这些规则是递归应用的
int x; namespace Y { void f(float); void h(int); } namespace Z { void h(double); } namespace A { using namespace Y; void f(int); void g(int); int i; } namespace B { using namespace Z; void f(char); int i; } namespace AB { using namespace A; using namespace B; void g(); } void h() { AB::g(); // AB is searched, AB::g found by lookup and is chosen AB::g(void) // (A and B are not searched) AB::f(1); // First, AB is searched. There is no f // Then, A, B are searched // A::f, B::f found by lookup // (but Y is not searched so Y::f is not considered) // Overload resolution picks A::f(int) AB::x++; // First, AB is searched. There is no x // Then A, B are searched. There is no x // Then Y and Z are searched. There is still no x: this is an error AB::i++; // AB is searched. There is no i // Then A, B are searched. A::i and B::i found by lookup: this is an error AB::h(16.8); // First, AB is searched. There is no h // Then A, B are searched. There is no h // Then Y and Z are searched // Lookup finds Y::h and Z::h. Overload resolution picks Z::h(double) }
允许多次找到相同的声明
namespace A { int a; } namespace B { using namespace A; } namespace D { using A::a; } namespace BD { using namespace B; using namespace D; } void g() { BD::a++; // OK: finds the same A::a through B and through D }
本节尚不完整 原因:N4861 6.5.3.2[namespace.qual] 的其余部分,尝试缩短他们的示例 |
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
CWG 215 | C++98 | :: 前的名称必须是类名称或命名空间名称,因此不允许在此处使用模板参数 |
名称必须指定一个类, 命名空间或依赖类型 |
CWG 318 | C++98 | 如果 :: 的右侧命名相同的类如左侧一样,限定名始终 被认为命名该类的构造函数 |
仅命名构造函数 在可接受的情况下 (例如,不在 详述类型说明符中) |