実引数依存の名前探索

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

実引数依存の名前探索じつひきすういぞんのなまえたんさくADL)とは、C++において関数呼出時に与えられた引数の型に依存して、呼び出す関数を探索 (lookup)する仕組みのことである。英語ではKoenig lookupargument dependent lookup (ADL)、argument dependent name lookupなどと呼ばれる。なお、Koenig lookupとは、この仕組みをAndrew Koenigが提案したことにちなむ。


概要[編集]

実引数依存の名前探索では、通常の名前探索では考慮されない他の名前空間も探索される可能性がある。探索される名前空間は実引数に依存する。直接・間接的に基底クラスを持つクラスAに対しては、その直接・間接的な基底クラスが「Aに関連するクラス」となる。Aが含まれる名前空間とAに関連するクラスが含まれる名前空間は、「Aに関連する名前空間」となる。A型のオブジェクトが関数呼出の際に実引数として用いられると、関連する名前空間からその関数が探索される。

探索によって見つかった宣言の集合は通常の名前探索で見付けた名前と関連する名前空間から見付けた名前とをまとめたものになる。その後この見付かった宣言の集合の中から多重定義の解決が行われる。なお、通常の名前探索でクラスのメンバ関数が探索された場合、実引数依存の名前探索は行われない。

たとえばこのような感じである:

namespace NS {
    class A {};
    void f(A) {}
}
int main() {
    NS::A a;
    f(a); //NS::f(a);と呼び出しているわけではないのに、NS::fが呼ばれる。
}

標準C++ライブラリでは、実引数依存の名前探索を主に演算子多重定義関数に対して用いている。たとえば次のプログラムは実引数依存の名前探索が無ければコンパイルできない。

#include <iostream>
#include <string>
int main() {
    std::string msg = "Hello World, where did operator<<() come from?";
    std::cout << msg << std::endl;
}

std::ostream& std::operator <<(std::ostream&, const std::string&)と宣言された関数は、実引数依存の探索によって見付かる(この関数はstd名前空間の中に存在することに注目)。ところで、std::endlは関数であるが、operator <<の引数として用いられているため、std::などといった完全な修飾が必要であることに注意。

インタフェース[編集]

C++ユーザからは、ADLで見つかる名前はクラスのインタフェースの一部と扱われる。

Standard Template Library (STL)では、一部のアルゴリズム関数がswap関数を修飾無しで呼ぶが、ADLで何も見つからなければ、結果的に同じ名前空間stdのswap関数が呼ばれる。一方、ADLで見つかったときはそちらが呼ばれる。つまり、とある名前空間hogeにクラスFooと関数swap(Foo&, Foo&)があって、アルゴリズム関数にそれらを渡すとhoge::swap(Foo&, Foo&)が使用されるという具合である。ただし、この挙動はC++03では規定されておらず、必ずしもそうなるとは限らない。C++0xで規定される見込みである。

問題点[編集]

ADLは自由関数(名前空間上に存在するクラスのメンバでない関数)もクラスのインタフェースとして扱う。つまり、名前空間に制限をもたらし、必要がなければ(名前空間などの指定で)完全に修飾された名前を用いる必要があることを意味する。逆の例として、標準C++ライブラリは2つの値の交換にstd::swapを修飾なしで呼ぶことが挙げられる。

別の案として、std::swapをユーザに多重定義させるという方法がある。次のコードは挙動が異なる。

完全修飾した名前で呼ぶ場合:

std::swap(a, b);

予めusingしておく場合:

using std::swap;
swap(a, b);

ただし、aとbはN::Aという型とする。

N::swap(N::A&, N::A&)が存在した場合、後者ではそれ呼ばれるが、前者では呼ばれない。さらに細かいことを言えば、仮に両方とも定義されていたら、前者ではstd::swap(N::A&, N::A&)が呼ばれるが、後者ではどちらにするか曖昧になる(名前の探索に失敗しコンパイルエラーになる)。

なお、std::swapを特殊化するという方法もあるが、特殊化しようとする型がテンプレートの場合に対応できない(自由関数で部分特殊化はできない)ために完璧ではない[1]

なお、std名前空間での多重定義は現在認められていない(特殊化は認められている)。

一般にADLに過度に依存すると意味の問題が起こる。あるライブラリL1がL1::foo(T)が呼び出される前提で未修飾のfoo(T)という呼出を行っているとする。別のライブラリL2も同様にfoo(T)の呼出を行っている。2つのライブラリを同時に使うと、L1::foo(T)が呼ばれなければならない場面でL2::foo(T)が呼ばれるなど互いに意図するとおりにならない可能性が生じる。しかし、L1が内部でL1::foo(T)とし、L2も内部で同様にL2::foo(T)と共に完全に修飾していれば、このようなADLの心配は全く起こらない(逆にADLを起こしたい場合はusing L1::foo; foo(x);のように書く)。

脚注[編集]

[ヘルプ]
  1. ^ Cryolite (2004年9月2日). “swapの特殊化・その他,細かいこと”. Cry's Diary. 2009年2月1日閲覧。

外部リンク[編集]