Namespaces
Variants

Qualified name lookup

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

  • 类成员(包括静态和非静态函数、类型、模板等),
  • 命名空间成员(包括其他命名空间),
  • 枚举项。

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

#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; // 错误:对'std'的非限定查找找到的是结构体
    ::std::cout << ::ok; // 正确:::std 找到的是命名空间 std
}

在能够对 :: 右侧名称执行名称查找之前,必须首先完成其左侧名称的查找(除非使用 decltype 表达式,或左侧无内容)。该查找(根据该名称左侧是否存在另一个 :: ,可能是有限定或无限定的)仅考虑命名空间、类类型、枚举以及其特化为类型的模板。如果在左侧找到的名称不指向命名空间、类、枚举或依赖类型,则程序非良构:

struct A
{
    static int n;
};
int main()
{
    int A;
    A::n = 42; // 正确:在 :: 左侧的 A 非限定查找会忽略该变量
    A b;       // 错误:A 的非限定查找找到了变量 A
}
template<int>
struct B : A {};
namespace N
{
    template<int>
    void B();
    int f()
    {
        return B<0>::n; // 错误:N::B<0> 不是类型
    }
}

当限定名称被用作 声明符 时,对于同一声明符中出现在该限定名称之后(但不在其之前)的名称,将在该成员所属类或命名空间的作用域内执行 非限定查找

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];    // 错误:对 X 的查找找到的是 ::X,而非 C::X
C::X C::arr[number], brr[number]; // 正确:arr 的大小为 50,brr 的大小为 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(); // ~ 后的名称 I 在与 :: 前的 I 相同的作用域中查找
                   //(即在 C 的作用域内,因此找到 C::I)
    q->I1::~I2();  // 名称 I2 在与 I1 相同的作用域中查找
                   //(即从当前作用域,因此找到 ::I2)
    AB x;
    x.AB::~AB();   // ~ 后的名称 AB 在与 :: 前的 AB 相同的作用域中查找
                   //(即从当前作用域,因此找到 ::AB)
}

目录

枚举项

若对左侧名称的查找得到一个 枚举 (无论是作用域枚举还是非作用域枚举),则对右侧的查找必须得到属于该枚举的枚举项,否则程序非良构。

(自 C++11 起)

类成员

如果左侧名称查找得到类/结构体或联合体名称,则 :: 右侧的名称将在该类的范围内进行查找(因此可能找到该类或其基类的成员声明),但存在以下例外情况:

  • 析构函数的查找方式如上所述(在 :: 左侧名称的作用域中查找)。
  • 用户定义转换 函数名中的转换类型标识符首先在类作用域中查找。若未找到,则在当前作用域中继续查找该名称。
  • 模板实参中使用的名称在当前作用域中查找(而非在模板名称的作用域中查找)。
  • using声明 中的名称也会考虑被同一作用域中声明的变量、数据成员、函数或枚举项名称所隐藏的类/枚举名称。

如果 :: 右侧的名称与左侧的类名相同,该名称指代该类的 构造函数 。此类限定名称仅能用于构造函数的声明以及在 using声明 中声明 继承构造函数 。在忽略函数名的查找中(即当查找 :: 左侧名称时,或在 详细类型说明符 基类说明符 中查找名称时),相同语法会解析为注入类名:

struct A { A(); };
struct B : A { B(); };
A::A() {} // A::A 命名构造函数,用于声明
B::B() {} // B::B 命名构造函数,用于声明
B::A ba;  // B::A 命名类型 A(在 B 的作用域中查找)
A::A a;   // 错误:A::A 不命名类型
struct A::A a2; // 正确:详细类型说明符中的查找忽略函数
                // 因此 A::A 仅命名从 A 作用域内可见的类 A
                //(即注入类名)

限定名称查找可用于访问被嵌套声明或派生类隐藏的类成员。对限定成员函数的调用永远不会是虚函数:

struct B { virtual void foo(); };
struct D : B { void foo() override; };
int main()
{
    D x;
    B& b = x;
    b.foo();    // 调用 D::foo(动态绑定)
    b.B::foo(); // 调用 B::foo(静态绑定)
}

命名空间成员

如果 :: 左侧的名称指向某个命名空间,或者 :: 左侧为空(此时指向全局命名空间),则出现在 :: 右侧的名称将在该命名空间的作用域中进行查找,但以下情况除外:

  • 模板参数中使用的名称在当前作用域内查找:
namespace N
{
    template<typename T>
    struct foo {};
    struct X {};
}
N::foo<X> x; // 错误:X 被查找为 ::X,而非 N::X

在命名空间 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,通过查找找到AB::g并选择AB::g(void)
              //(不搜索A和B)
    AB::f(1); // 首先搜索AB,未找到f
              // 然后搜索A和B
              // 通过查找找到A::f和B::f
              //(不搜索Y,因此不考虑Y::f)
              // 重载决议选择A::f(int)
    AB::x++;  // 首先搜索AB,未找到x
              // 然后搜索A和B,未找到x
              // 接着搜索Y和Z,仍未找到x:此处产生错误
    AB::i++;  // 搜索AB,未找到i
              // 然后搜索A和B,通过查找找到A::i和B::i:此处产生歧义错误
    AB::h(16.8); // 首先搜索AB,未找到h
                 // 然后搜索A和B,未找到h
                 // 接着搜索Y和Z
                 // 查找找到Y::h和Z::h,重载决议选择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++; // 正确:通过 B 和 D 都能找到同一个 A::a
}

缺陷报告

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

缺陷报告 适用标准 已发布行为 正确行为
CWG 215 C++98 :: 前的名称必须是类名或命名空间名,
因此不允许在此处使用模板参数
该名称必须指定类、
命名空间或依赖类型
CWG 318 C++98 :: 右侧名称与左侧类名相同,
该限定名称始终被视为该类的构造函数
仅在可接受时命名构造函数
(例如不在详细类型说明符中)

参见