命名空间
变体
操作

限定名查找

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
 
 

限定 名是出现在作用域解析运算符 :: 右侧的名称 (另见 限定标识符)。 限定名可以指代

  • 类成员 (包括静态和非静态函数、类型、模板等),
  • 命名空间成员 (包括另一个命名空间),
  • 枚举器。

如果 :: 的左侧没有任何内容,则查找仅考虑 全局命名空间作用域 中的声明。 这使得即使名称被局部声明隐藏,也可以引用这些名称

#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
}

[编辑] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
CWG 215 C++98 :: 前的名称必须是类名称或命名空间
名称,因此不允许在此处使用模板参数
名称必须指定一个类,
命名空间或依赖类型
CWG 318 C++98 如果 :: 的右侧命名相同的类
如左侧一样,限定名始终
被认为命名该类的构造函数
仅命名构造函数
在可接受的情况下 (例如,不在
详述类型说明符中)

[编辑] 参见