C++中Name lookup的過程就是一個通過name找到對應申明的過程。對於函數來說,name lookup可能會匹配出很多個申明,然後如果是函數再通過Argument-dependent lookup去查找,如果是函數模板那麼通過Template argument deduction去查找,最後一步通過overload resolution去解決。
name lookup分為兩種,qualified name lookup和unqualified name lookup,第一個name的左邊是否還有::符號。
一個Qualified name出現在::的右邊,它可以包括:類成員(方法,類型,模板等);命名空間成員(另一個命名空間);枚舉 。
規則如下:
::左邊如果沒有其他元素的話,那麼默認使用頂級命名空間,示例如下:
#includenamelookup的順序是從左到右的,::左邊的元素只會被當做命令空間,類,枚舉,模板,示例如下:int main() { struct std{}; std::cout << fail ; // Error: unqualified lookup for 'std' finds the struct ::std::cout << ok ; // OK: ::std finds the namespace std }
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 }如果一個qualified name作為一個一個聲明,那麼在它之後的所有相同申明的name的lookup都會和它一樣,在他之前的除外。
class X { }; constexpr int number = 100; class C { class X { }; static const int number = 50; static X arr[number]; }; X C::arr[number], brr[number]; // Error // Every name in the declarator C::arr[number] after C::arr // is looked up within C::, but the names before C::arr are unaffected, // The names in the second declarator (brr[number]) are also unaffected // equivalent to: // ::X C::arr[C::number], brr[::number] C::X C::arr[number], brr[number]; // Compiles, size of arr is 50, size of brr is 100如果::的右邊是一個析構函數,那麼這個析構函數的name lookup將會和::左邊的name lookup保持一致。
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 }如果::的左右兩邊都是相同的類名,那麼他只能被當做一個構造函數。但是當使用Elaborated type specifier時後面的函數就會被忽略掉。
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)
qualified name lookup可以用來訪問被嵌套或者派生隱藏的類成員
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) }當::的左邊指定了命名空間,或者::左邊沒有name(使用頂級命名空間),::的右邊就使用該命名空間,除了下面這種情況,即name被用作模板參數。
namespace N { template在命名中間嵌套引用中,Qualified name lookup遞歸執行,先在第一層中lookup,如果沒有就在引用的命名空間中lookup,一直遞歸下去:struct foo {}; struct X {}; } N::foo x; // error: X is looked up as ::X, not as N::X
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 }