Argument Dependent Lookup

C++言語の持つオシャレな機構の一つにADL(Argument Dependent Lookup)というものがあります。

和訳すると「実引数依存の名前検索」となり、読んで字の如く、「実引数」に依存した「名前検索」となります。なるほどわからん。

ADLの挙動

コードを見ながら順を追って理解してみましょう。

funcという関数が呼ばれるコードですが、これをただコンパイルするとエラーになります。

なぜならば……funcという関数は見る限りどこにも定義されていないからです!!

 

…………。

 

 

では、定義しましょう。

今度はコンパイルエラーにならずに無事、func関数が呼び出せます。

当たり前すぎて、今、自分が何を読んでいるのか分からなくなってきたのではないでしょうか。明日はどっちでしょうか。

先へ進みます。

func関数をnamespace ns1で囲みました。

このコードは再度コンパイルエラーになります。当たり前ですね。

9行目で呼ばれているfuncのシンボルをグローバルネームスペースから探そうとするのですが、実際に定義されているfuncはns1ネームスペースの中にある為、通常の名前検索ではヒットしない為です。

なのでネームスペースを指定した上で呼び出す必要があります。

まったく何を読まされているんだという感じでしょうが、ここまではウォーミングアップです。おつかれさまでした。

ではもっと具体的な実装コードを見てみましょう。

namespace ns1に色情報を持つ構造体と、内容を出力する関数を定義しました。

このmain関数をよく見て下さい。

Colorの型にはns1::という指定が付いていますが、printはネームスペースの指定を付けずに呼び出しています。

しかし、このコードはコンパイルエラーになりません。

なぜならc++様はある型のオブジェクトが関数呼出の際に実引数として用いられると、関連するネームスペースからも、その関数が探索されるという仕様だからです。

今回の場合は、printの引数で渡されているcの型がns1::Colorで、ns1ネームスペースに関係があるため、printという関数のシンボル検索グローバルネームスペースからだけではなく、関係があるns1からも検索するという挙動になります。

これが「実引数依存の名前検索(ADL)」の挙動です。

では「関連するネームスペース」とは何でしょうか。

引数がクラスのメンバだった場合

そのクラス自身および基底クラス、そしてそのクラスを囲むネームスペースです。

例えばstd::vector::iteratorはクラスのメンバですので、vectorとstdが関連するネームスペースになりますね。

引数がネームスペースのメンバだった場合

あるネームスペースの中で定義されているだけのクラスの場合、関連するネームスペースはそのクラスを囲むネームスペースです。そのクラスが継承していれば継承元のクラスに対して、同様の条件に基いて関連するネームスペースが決定します。

注意点

前述のとおりADLは人間の目では一見しただけではわからないネームスペースからもシンボルを探そうとします。

その結果、本来意図しない関数が呼び出される危険性もあります。

でも大丈夫、予期せぬADLの防止策もちゃんとあります。

ADL Firewall

また、この辺りの注意点について詳しく解説しているサイトも多いので色々参照してみるとよいでしょう。

google検索[ADL c++ 注意]

 

なぜ存在するのか

複雑性を増すだけに思えるこのADLという仕組みがなぜ必要なのか。それは演算子をネームスペースの修飾無しで呼べるようにするためです。

ADLという仕組みがないと

の部分を

と、何とも味わい深い呼び出し方をするしかなくなってしまいます。

これはストリーム演算子含むあらゆる演算子に言えることです。

よく登場する<<演算子も本来stdのネームスペースの中にあるので、ADLが無いと直感的に呼び出せないのです。

 

またSTLのswapのように(と言っても他に良い例を知らないですが)あえてADLの挙動を利用して汎用的な処理ができるようにしているコードもあります。

STLの中で呼ばれる修飾無しのswapはユーザー定義のものがADLで見つかればそれを呼ぶし無ければstd::swapが呼ばれるという実装になっています。

まとめ

  1.  ADLの挙動を理解しよう
  2. ADLによる名前解決の結果、実行するまでバグと分からないことがある
  3. ADLのお陰で演算子が完結に書けるというメリットもある
  4. 世の中にはADLの挙動を意図的に利用した悪魔的なコードもある
Pocket