限定名查找
一个限定名是指出现在作用域解析运算符 ::
右侧的名称(参见限定标识符)。一个限定名可以引用
- 类成员(包括静态和非静态函数、类型、模板等),
- 命名空间成员(包括另一个命名空间),
- 枚举器。
如果 ::
的左侧没有任何内容,则查找只考虑全局命名空间作用域中的声明。这使得即使这些名称被局部声明隐藏,也可以引用它们。
#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 起) |
[编辑] 类成员
如果左侧名称的查找结果是类/结构体或联合名称,则 ::
右侧的名称在该类的作用域中查找(因此可能找到该类或其基类的成员声明),但有以下例外情况:
- 析构函数如上所述查找(在
::
左侧名称的作用域中)。 - 用户定义转换函数名称中的转换类型 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
和 N
的所有传递性内联命名空间成员中找到的using 指令命名的所有命名空间中的声明。这些规则递归应用。
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++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 215 | C++98 | :: 前的名称必须是类名或命名空间名称,因此那里不允许使用模板参数 |
该名称必须指定一个类, 命名空间或依赖类型 |
CWG 318 | C++98 | 如果 :: 的右侧命名了相同的类作为左侧,限定名总是 被认为是该类的构造函数 |
仅命名构造函数 当可接受时(例如,不在 详述类型说明符) |