C++ プログラミング言語 では、引数依存検索(ADL)または引数依存名前検索[1]は、関数呼び出しに渡された引数の型に応じて、非修飾関数名を検索する場合に適用される。この動作は、アンドリュー・ケーニッヒによるものとされることが多いため、ケーニッヒ検索としても知られているが、彼はその発明者ではない。[2]
引数依存の検索では、通常の検索では考慮されない他の名前空間が検索されることがあります。検索される名前空間のセットは、関数の引数の型によって異なります。具体的には、ADL プロセス中に検出され、関数名の解決に考慮される宣言のセットは、通常の検索で見つかった宣言と、関数の引数の型に関連付けられた名前空間のセットを検索して見つかった宣言の和集合です。
例
ADL の例は次のようになります。
名前空間NS {
クラスA {};
void f ( A & a , int i ) {}
} // 名前空間 NS
int main () { NS :: A a ; f ( a , 0 ); // NS::f を呼び出します。}
たとえ主要関数は名前空間NS内になく、名前空間NSもスコープ内にない場合、関数NS::f(A&, int)関数呼び出しステートメント内の実際の引数の宣言された型が原因で見つかります。
C++ 標準ライブラリの一般的なパターンは、このようにして見つかるオーバーロードされた演算子を宣言することです。たとえば、この単純なHello Worldプログラムは、ADL がなければコンパイルされません。
#include <iostream> #include <文字列>
int main () { std :: string str = "hello world" ; std :: cout << str ; }
を使用することは、修飾子なしで<<呼び出すことと同じです。ただし、この場合、 に機能する operator<< のオーバーロードは名前空間内にあるため、それを使用するには ADL が必要です。
operator<<std::stringstd
次のコードは ADL なしでも動作します (いずれにせよ適用されます)。
#include <iostream>
int main () { std :: cout << 5 ; }
これは、整数の出力演算子std::ostreamが の型である クラスのメンバー関数であるため機能しますcout。したがって、コンパイラはこのステートメントを次のように解釈します。
std :: cout .演算子<< ( 5 );
これは通常の検索中に解決できます。ただし、たとえばconst char *オーバーロードされた関数は名前空間operator<<内の非メンバー関数であるstdため、正しい検索には ADL が必要であることを考慮してください。
/* 引数型 std::cout から派生した ADL を使用して、指定された char 文字列を期待どおりに出力します */
operator << ( std :: cout , "Hi there" )
/* void const* を受け取る operator<< の ostream メンバー関数を呼び出します。
これにより、char 文字列の内容ではなく、指定された char 文字列のアドレスが出力されます */ std :: cout . operator << ( "Hi there" )
文字列を処理するための、名前std空間オーバーロードされた非メンバーoperator<<関数も別の例です。
/*operator<<(std::cout, str) と同等です。コンパイラは、str パラメータの型が std::string で、std::cout であるため、ADL を使用して std 名前空間を検索します */
std :: cout << str ;
Koenig が個人的なメモで指摘しているように、[2]operator<< ADL がなければ、ステートメントが名前空間で見つかるかどうか明示的に指定していないため、コンパイラは見つけられないというエラーを表示しますstd。
インターフェース
ADL によって検出された関数は、クラスのインターフェイスの一部とみなされます。C++ 標準ライブラリでは、いくつかのアルゴリズムが名前空間swap内からへの非修飾呼び出しを使用しますstd。その結果、他に何も見つからない場合はジェネリック関数が使用されますが、これらのアルゴリズムが も含む別の名前空間で見つかったstd::swapサードパーティのクラス で使用される場合は、 のオーバーロードが使用されます。
Fooswap(Foo&, Foo&)swap
批判
ADL では、クラスの外部で定義された関数がそのクラスのインターフェースの一部であるかのように動作することが実用的になりますが、名前空間の厳密さが薄れるため、本来は必要のない完全修飾名の使用が必要になる場合があります。たとえば、C++ 標準ライブラリでは、std::swap2 つの値を交換するのに修飾なしの呼び出しを多用しています。その考え方は、独自の名前空間で独自の を定義しswap、それを標準ライブラリのアルゴリズム内で使用できるというものです。言い換えると、
名前空間N {
構造体A {};
} // 名前空間 N
Aa ; Ab ;
std :: swap ( a , b );
の行動と同じかもしれないし、そうでないかもしれない
std :: swapを使用します。swap ( a , b ) ;
(ここでa、 と はb型ですN::A) が存在する場合N::swap(N::A&, N::A&)、上記の例の 2 番目は を呼び出しますが、最初の例は を呼び出しません。さらに、何らかの理由でN::swap(N::A&, N::A&)と の両方std::swap(N::A&, N::A&)が定義されている場合、最初の例は を呼び出しますstd::swap(N::A&, N::A&)が、2 番目の例は があいまいになるためコンパイルされませんswap(a, b)。
一般に、ADL に過度に依存すると、意味上の問題が発生する可能性があります。あるライブラリ がL1への非修飾呼び出しにfoo(T)1 つの意味があると想定し、別のライブラリ がL2別の意味があると想定する場合、名前空間は有用性を失います。ただし、 が1 つの意味を持つことをL1想定し、 も同様に想定する場合、競合は発生しませんが、ADL が邪魔にならないように、 への呼び出し
は完全修飾 (つまり ではなく)する必要があります。L1::foo(T)L2foo(T)L1::foo(x)using L1::foo; foo(x);
参考文献
- ^ 「ワーキングドラフト、プログラミング言語 C++ の標準」(PDF)。JTC1/SC22/WG21。 2005 年 10 月 19 日。第 3.4.2 章 –引数依存の名前検索– p. 2。 2005 年 12 月 14 日時点のオリジナル(PDF)からアーカイブ。2012 年3 月 13 日閲覧。
- ^ ab 「引数依存ルックアップに関する個人的なメモ」。2012年5月3日。2018年3月17日時点のオリジナルよりアーカイブ。2014年2月7日閲覧。
外部リンク
- 引数依存検索
- クラスには何があるか? –ハーブ・サッターによるインターフェース原則
- 名前空間とインターフェース原則 ( Herb Sutter著)
- なぜ名前空間が嫌いなのか、Danny Kalev 著
- 「控えめな提案: ADL の修正 (改訂 2)」ハーブ・サッター著
