名字查找
名字查找,是当程序中出现一个名字时,将其与引入它的声明
联系起来的过程
例如,为编译 std::cout << std::endl;,编译器进行了:
- 名字 std 的无限定的名字查找,找到了头文件 中的命名空间 std 的声明
- 名字 cout 的有限定的名字查找,找到了命名空间 std 中的一个变量声明
- 名字 endl 的有限定的名字查找,找到了命名空间 std 中的一个函数模板声明
- 名字 operator <<的
实参依赖查找
,找到了命名空间std中的多个函数模板声明;和名字std::ostream::operator<<
的有限定名字查找,找到了std::ostream
类中的多个成员函数声明
对于函数和函数模板中的名字,名字查找可以将同一个名字和多个声明联系起来,而且可能从实参依赖查找中得到额外的声明。还会进行模板实参推导,并将声明的集合交给重载决议,由它选择所要使用的那个声明。成员访问
的规则只会在名字查找和重载解析之后才被考虑,如果适用的话
- 对所有其他的名字(变量,命名空间,类等等)的名字查找,程序只有在只产生单个声明的情况下才能编译。
- 对某个作用域中的名字进行查找将寻找到该名字的所有声明,但有一种例外,被称作“ struct hack ”或“类型/非类型名字隐藏”:
- 同一作用域中,某个名字的一些出现可以代表非 typedef 的 class/struct/union/enum 声明,
- 而其他出现要么全都代表同一个变量、非静态数据成员 (C++14 起)或者枚举项,要么全都代表可能重载的函数或函数模板名。
- 此情况下无错误,但类型名在查找中被隐藏(代码必须用详述类型说明符来访问它)。
查找的类型
有限定的名字查找
出现在作用域解析操作符 :: 右边的名字是限定名(参阅有限定的标识符)。 限定名可能代表的是:
- 类的成员(包括静态和非静态函数、类型和模板等)
- 命名空间的成员(包括其它的命名空间)
- 枚举项
如果::
左边为空,则查找过程仅会考虑全局命名空间作用域中作出(或通过 using 声明引入到全局命名空间中)的声明。这使得即使被局部声明隐藏的名字也能够被访问:
#include <iostream>
int main() {
struct std{
};
std::cout << "fail\n"; // 错误:对 'std' 的无限定查找找到 struct
::std::cout << "ok\n"; // 正确: ::std 找到命名空间 std
}
只有完成对 :: 左边的名字的查找(除非左边所用的是 decltype 表达式或左边为空),才能对其右边的名字进行名字查找。
struct A {
static int n;
};
int main() {
int A;
A::n = 42; // 正确:对 :: 左边的 A 的无限定查找忽略变量
A b; // 错误:对 A 的无限定查找找到了变量 A
}
有限定的标识符
有限定的标识表达式是在无限定的标识表达式前面带上作用域解析运算符 ::,以及可选地带上一系列以作用域解析运算符分隔的 枚举、 (C++11 起)类或命名空间的名字,或者 decltype 表达式 (C++11 起)
- 表达式 std::string::npos 是命名在命名空间 std 内的类 string 中的静态成员 npos 的表达式
- 表达式·
::tolower
命名的是全局命名空间内的函数 tolower - 表达式
::std::cout
指名 std 命名空间(顶层命名空间)中的全局变量 cout