名字查找
C++ 有一个准则,就是标识符要先申明,再应用。每一个标识符在应用的时候,都要找到它对应的申明。这个过程,就是名字查找。
在程序的不同构造中,名字查找的规定也不尽相同。上面简述如下。
Unqualified name lookup
简略地说,就是独自一个标识符的查找。
名字空间中对标识符的应用
名字空间中的应用的标识符须要在应用前,在以后或蕴含它的名字空间中被申明。
名字空间中定义的函数中
这包含函数名之后呈现的任何符号,包含参数表中呈现的类型、默认参数等。
其申明须要呈现在应用之前,顺次查找以后块(block),蕴含以后块的块,函数属于的 namespace,该 namespace 的父 namespace。
namespace A { namespace N { void f(); }}void A::N::f() { i = 5; // i 的申明会在以下中央被查找: // 1) A::N::f 中,i 的只用之前 // 2) namespace N // 3) namespace A // 4) 全局名字空间,A::N::f 的定义之前}
类定义中的名字
类定义中的名字按其是否在一个 complete-class context ,查找跪在不同。
Complete-class context 是指:函数体、函数的默认参数、noexcept-specifier,成员初始化
在 complete-class context 外,申明要在应用之前,查找范畴包含:以后类、基类的成员、蕴含以后类定义的类(如有)或其基类成员、蕴含以后类的函数(如有)、蕴含以后类的namespace。
namespace M { class B { };}namespace N { class Y : public M::B { class X { int a[i]; }; };}// 会在以下地位查找 i 的申明:// 1) class N::Y::X 中,i 应用之前// 2) class N::Y 中, N::Y::X 定义之前// 3) N::Y’s 的基类 M::B// 4) 名字空间 N, N::Y 定义之前// 5) 全局名字空间, N 定义之前
在 complete-class context 中,当查找类成员时,并不要求成员申明在标识符应用之前,查找范畴包含:以后块或蕴含的块(如有)、以后类成员或基类的成员、蕴含以后类定义的类(如有)的成员或其基类成员、蕴含以后类的函数(如有)、蕴含以后类的namespace。
class B { };namespace M { namespace N { class X : public B { void f(); }; }}void M::N::X::f() { i = 16;}// 会在以下地位查找 i 的申明:// 1) M::N::X::f, i 的应用之前// 2) class M::N::X// 3) M::N::X 的基类 B// 4) namespace M::N// 5) namespace M// 6) global scope, M::N::X::f 定义之前
friend
友元函数如果定义在类中,那么其定义的名字查找规定与类成员统一。
如果友元函数时类成员,那么标识符将首先在该函数所在的类中查找,除非是用于 declarator-id 中的 template-argument 的标识符。
struct A { typedef int AT; void f1(AT); void f2(float); template <class T> void f3();};struct B { typedef char AT; typedef float BT; friend void A::f1(AT); // parameter type is A::AT friend void A::f2(BT); // parameter type is B::BT friend void A::f3<AT>(); // template argument is B::AT};
默认参数、mem-initializer
在默认参数、mem-initializer 的 expression 中,函数参数是可见的,并且会笼罩外层同名申明。
枚举
枚举定义中,已定义的 enumerator 在后续定义中可见
static member
类的动态成员定义中,名字查找规定同类成员函数
名字空间的数据成员
如果名字空间的数据成员的定义呈现在名字空间之外,其定义中的标识符查找形式依照其定义在其名字空间中时的规定进行。
namespace N { int i = 4; extern int j;}int i = 2;int N::j = i; // N::j == 4
Argument-dependet name lookup
此规定用于函数调用。
typedef int f;namespace N { struct A { friend void f(A &); operator int(); void g(A a) { int i = f(a); // f is the typedef, not the friend function: equivalent to int(a) } };}
只有在函数是 unqualified-id 的时候会应用该查找形式。
namespace N { struct S { }; void f(S);}void g() { N::S s; f(s); // OK: calls N::f (f)(s); // error: N::f not considered; parentheses prevent argument-dependent lookup}
当通常的 unqualified name lookup 能够找到
- 一个类成员
- 一个块级函数申明(除去 using-declaration)
- 非函数或函数模板的申明时
时,Argument-dependent name lookup 不失效。否则,查找的后果将蕴含一般查找后果与 Argument-dependent name lookup 后果的并集。
Argument-dependent name lookup 理论是在参数类型所在的名字空间中查找,并且:
- 疏忽 using-directive
- 非函数与函数模板的申明将被疏忽
- 定义在类中的有元函数能够被找到
Qualified name lookup
形如 A::B::C
模式,对 B
, C
的查找都是 qualified name lookup。
以 ::
开始的在全局名字空间中查找。
如果 ::
前的局部为一个枚举,其后应为该枚举中的 enumerator 。
...
type-name ::
~
type-name 中(两个 type-name 不肯定统一),第二个 type-name 与 第一个 type-name 在雷同的范畴内查找。
当 ::
前不是枚举时,它可能是一个类,或名字空间。
类成员
当 ::
前是一个类时,在类成员及基类中查找。(留神,是基类,不是基类的成员。)然而有如下几个例外:
- 对析构函数查找(见上)
- conversion-function-id 中的 conversion-type-id 的查找同类成员拜访
- template-id 中的 template-argument 在整个 postfix-expression 的上下文中查找
- using-declaraion 中的名字,能够找到以后被暗藏类或enumeration
名字空间成员
对名字空间成员的查找,会首先名字空间自身的成员。只有当此查找无后果时,才会去查找通过 using-directive 引入的成员。
Elaborated type specifiers
形如 class A
, struct B::C
等。
对于 unqualifed-id ,依照 unqualified name lookup 规定查找,然而疏忽所有非类型的申明。
对 class
, struct
, union
,如果找不到则可能申明一个新的类。
如果其为 qualified-id ,那么应用 Qualified name lookup 规定查找,同时疏忽所有非类型的名字。此时,即便找不到也不会申明新的类型。
Class member access
a.b
, p->c
如果 id-expression
是一个 unqualified-id,那么在对象类中进行查找。
a.B::b
模式中,B
将首先在 a 的类中查找。如找不到,则会在整个 postfix-expression 上下文中查找。
对 simple-template-id 中的 template-arguments 在整个 postfix-expression 上下文中查找。
在 conversion-function-id 中,其 conversion-type-id 在对象类中查找。如找不到,则在整个 postfix-expression 中查找。所有查找过程中,类型(或类型模板)以外的名字被疏忽。