問題の歴史: ADL(引数依存検索)は、C++でオペレーターのオーバーロード(特にユーザー定義型に対するoperator<<およびoperator>>)をサポートするために初めて登場しました。目的は、名前空間とユーザー定義型を混合した際に関数を正しく見つけることです。
問題: 標準の関数探索メカニズムは、呼び出しポイントとは異なる名前空間で宣言された関数を「見逃す」可能性があります。ADLはこの問題を解決します:コンパイラは、関数の引数の型に関連する名前空間を考慮して名前を解決します。このメカニズムは、予期しないオーバーロードの選択やあいまいさを引き起こすことがあります。
解決策: ユーザー定義の名前空間からのオブジェクトを引数として持つ関数が呼び出されると、コンパイラは現在のスコープだけでなく、引数の型に関連するすべての名前空間でも適切なオーバーロードを検索します。
コードの例:
namespace lib { struct Widget {}; void do_something(const Widget&) { std::cout << "Widget" << std::endl; } } using lib::Widget; void call(const Widget& w) { do_something(w); // do_somethingはADLを介して見つかります }
主な特徴:
同じ名前の関数を2つの名前空間で宣言し、2番目のオブジェクトを渡した場合、どちらが呼び出されますか?
引数の名前空間の関数が、引数に適合する場合ADLのおかげで選択されます:
namespace A { struct S {}; void f(const S&) { std::cout << "A!"; } } namespace B { struct S {}; void f(const S&) { std::cout << "B!"; } } A::S a; B::S b; f(a); // A::fがADLを介して呼び出される f(b); // B::fがADLを介して呼び出される
ADLはテンプレート関数に対しても機能しますか?
はい、テンプレート関数が引数の型と一致する名前空間で定義されている場合、ADLはその型で呼び出されるときにそれを見つけます。
関数ポインタに対してADLは機能しますか?
いいえ、ADLは関数へのポインタを取得する際(例:そのアドレスを取得する際)には適用されません。関数を直接呼び出す場合のみです。
プロジェクトは複数の外部ライブラリを接続し、各名前空間に独自のprint()がありました。メインコードでは異なるクラスのオブジェクトを使用してprint()が使用されていました。ADLにより、コンパイラは異なる名前空間から関数を「選択」し始めました。
利点:
欠点:
qualified call(名前空間を明示する)を使用:
lib::do_something(w); // 明示的!
利点:
欠点: