C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
有限定的名字查找
限定名,是出现在作用域解析操作符 ::
右边的名字(参阅有限定的标识符)。
限定名可能代表的是:
- 类的成员(包括静态和非静态函数、类型和模板等)
- 命名空间的成员(包括其它的命名空间)
- 枚举项
若 ::
左边为空,则查找过程仅会考虑全局命名空间作用域中作出(或通过 using 声明引入到全局命名空间中)的声明。这样一来,即使局部声明隐藏了该名字,也能够访问它:
在能对 ::
右边的名字进行名字查找之前,必须完成对其左边的名字的查找(除非左边所用的是 decltype 表达式或左边为空)。对左边的名字所进行的查找,根据这个名字左边是否有另一个 ::
可以是有限定或无限定的,但其仅考虑命名空间、类类型、枚举和能特化为类型的模板:
struct A { static int n; }; int main() { int A; A::n = 42; // 正确:对 :: 左边的 A 的无限定查找忽略变量 A b; // 错误:对 A 的无限定查找找到了变量 A }
以限定名为声明符时,对同一声明符中随该限定名之后,而非其之前的名字的无限定查找,是在成员的所在类或命名空间的作用域中进行的:
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]; // OK : 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 起) |
类成员
若对左边的名字的查找结果是某个类、结构体或联合体的名字,则 ::
右边的名字在该类、结构体或联合体的作用域中进行查找(因此可能找到该类或其基类的成员的声明),但有以下例外情况:
- 析构函数按如上所述进行查找(即在 :: 左边的名字的作用域中查找)
- 用户定义转换函数名中的转换类型标识( conversion-type-id ),首先在该类类型的作用域中查找。若未找到,则在当前作用域中查找该名字。
- 模板实参中使用的名字,在当前作用域中查找(而非在模板名的作用域中查找)
- using 声明中的名字,还考虑在当前作用域中声明的变量、数据成员、函数或枚举项所隐藏的类或枚举名
本节未完成 原因:上述内容的小示例 |
若 ::
右边所指名的是和其左边相同的类,则右边的名字表示的是该类的构造函数。这种限定名仅能用在构造函数的声明以及引入继承构造函数的 using 声明中。在所有忽略函数名的查找过程中(即在查找 ::
左边的名字,或查找详述类型说明符或基类说明符中的名字时),则将同样的语法解释成注入类名( injected-class-name ):
struct A { A(); }; struct B : A { B(); }; A::A() { } // A::A 指定的是构造函数,用于声明 B::B() { } // B::B 指定的是构造函数,用于声明 B::A ba; // B::A(在 B 的作用域中查找)指定的是类型 A 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
和 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++; // OK : 通过 B 和 D 找到同一个 A::a }
本节未完成 原因:3.4.3.2[namespace.qual] 中的剩余内容,尝试缩短其示例 |