限定名称查找
限定名称是在作用域解析运算符 ::
右侧出现的名称(另请参阅 限定标识符)。限定名称可以指代
- 类成员(包括静态和非静态函数、类型、模板等),
- 命名空间成员(包括另一个命名空间),
- 枚举器。
如果 ::
左侧没有内容,则查找只考虑 全局命名空间范围 中的声明。这使得即使名称被局部声明隐藏,也可以引用这些名称
#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 起) |
[编辑] 类成员
如果左侧名称的查找结果是类/结构或联合名称,则在该类的作用域中查找 ::
右侧的名称(因此可以找到该类或其基类的成员声明),以下情况除外
- 析构函数的查找方式如上所述(在
::
左侧名称的作用域中)。 - 在 用户定义的转换 函数名称中的转换类型标识符首先在类的作用域中查找。如果未找到,则在当前作用域中查找该名称。
- 模板参数中使用的名称在当前作用域中查找(而不是在模板名称的作用域中)。
- 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-directives 命名的命名空间以及在 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 | 如果 :: 右侧的名称与左侧的名称相同作为左侧的名称,限定名总是 被认为命名了该类的构造函数 |
只命名构造函数 当可接受时(例如,不在 详细的类型说明符中) |