関数呼び出し
関数名の探索の勉強.関数呼び出しではどの関数が呼び出されるかを決めなければならない.
まず関数が呼び出されたスコープ内で呼び出される関数の名前の探索が行われる.このときADL(Argument Dependent name Lookup)によって引数の型が定義された名前空間も探索空間に追加される.同じ名前のものが見つかったら関数名の探索は終了し,見つからないなら上のスコープへ…と言う風になっている.
さらに継承とかテンプレートの特殊化とかが絡むと複雑なので少しまとめてみた(まとまっていない).
#include <iostream> using namespace std; void g() { cout << "global" << endl; } namespace ns1 { void g() { cout << "ns1" << endl; } struct Base {}; void f(const Base&) { cout << "ns1" << endl; } } namespace ns2 { template <class T> struct ClassT {}; } template <class T> void f(const ns2::ClassT<T>&) { cout << "global" << endl; } namespace ns3 { struct Derived : ns1::Base { void f() { g(); } }; void f(const ns1::Base&) { cout << "ns3" << endl; } void f(const ns2::ClassT<Derived>&) { cout << "ns3" << endl; } } struct Global {}; void g(const Global&) { cout << "global" << endl; } namespace ns4 { template <class T> void g(const T&) { cout <<"ns4" << endl; } void f(const Global& gl) { g(gl); } } template <class T> void f(const T&) { cout << "template <class T> f" << endl; } template <> void f(const Global&) { cout << "template <> f<Global>" << endl; } void f(const Global&) { cout << "func f" << endl; } namespace ns5 { using namespace ns1; // void f() { g(); } } namespace ns6 { using ns1::g; void f() { g(); } }; int main() { ns1::Base b; f(b); // #1 ns1 ns2::ClassT<ns3::Derived> classT; f(classT); // #2 ns3 ns3::Derived d; f(d); // #3 曖昧 d.f(); // #4 global Global gl; ns4::f(gl); // #5 global f(gl); // #6 func f ns5::f(); // #7 曖昧 ns6::f(); // #8 ns1 }
#1はbの型がns1::BaseなのでADLによってns1::fが呼ばれる.
#2はADLでns3::fが呼ばれるのだが,ADLではテンプレート引数となる型の名前空間も探索空間に追加される.
#3はns1::fとns3::fで呼び出しが曖昧となる.ADLは基底クラスの名前空間も探索空間に追加する.
#4.ns3::Derived::f中でgが呼ばれる.しかしns3::Derived内にgは見当たらないので次の探索空間を探す.次の探索空間はns1::Baseでその次の探索空間はns3になる.ここにもgはないのでグローバル空間を探索することになる.ns3::Derivedはns1::Baseを継承しているがADLとは違うのでns1内は探索しない.もしns3::Derived::fが存在せずns1::Base::fが代わりにあるとns1::Base -> ns1になる.
#5もADL.グローバル名前空間もADLが働く.そのため探索空間には::gと,template
#6.名前探索中はテンプレートの特殊化は起こらない.そのため見つかるのはテンプレート関数のfとグローバルのfである.後者の方がより適合するので後者が呼ばれる.
#7はusing directive.using directiveはただ名前を見えるようにしているだけなので,見えるようになった名前はグローバル空間が探索空間になったときに追加される(はず).そのため::gとns1::gが衝突する.
#8はusing declaration.using declarationは名前を宣言する.つまりそれを記述した名前空間に名前を追加する.よってns1::gはns6::gにも存在する用に見える.
継承あたりが少しややこしいかなと個人的には思う.仮想関数が入るともっとややこしい.