ADLとは|問題と危険性、回避方法(明示的回避と事前回避)本記事ではADLの概要を説明します。ADLの利点や問題点、危険性、ADLの回避方法についても詳細に解説していきます。実引数依存の名前探索)は実引数の型を活用した名前解決の特別なルールです。ADLは関数呼び出し時の実引数の型を基準に関数名を探索します。その探索の範囲は、その実引数が属する名前空間にまで及ぶ点が最大の特徴です。`ns::f(X)`関数が呼ばれる}上記main関数内で呼び出しているf関数は、一見するとグローバル空間のf関数を呼び出しているように見えますが、実際には名前空間ns内部のf関数を呼び出します。f関数の名前探索は実引数型Xが属する名前空間nsを基準に行われるため、このような呼び出しが行われるのです。これを実現する仕組みがADLというわけです。lookup」と呼ばれることもあります。ADLの利点と活用例ADLの活用によって面倒な名前空間の明示を省略できるようになるという利点が生まれます。実際に、ADLはC++の標準入出力ストリームの<<演算子にも活用されています。std::string("hello");上記の<<演算子は実は名前空間stdの内部で定義されています。ADLの仕組みが無いと、以下のように名前空間を明示した形で演算子を呼び出さなければならなくなります。string&)演算子関数の定義を探索することが可能になるため、先程の面倒な名前空間の明示も不要となります。ADLの発動パターンテンプレート関数への探索ADLは一定の条件を満たせば、C++標準ライブラリのテンプレート関数に対しても働くため、以下のような記述も可能となります。{});利用される実引数型と同一の名前空間で宣言されたテンプレート関数は、ADLによる探索の対象となるため、このようなstdの省略が可能となっているのです。多重定義時やユーザ定義関数との混在時に余計な混乱が発生することがあるため、このような省略の多用はオススメできません。詳しい理由は次の節で説明します。begin関数やend関数へのADLはstd以外の名前空間で定義された基本型に対しては働かない点に注意が必要です。'std::begin'?この場合は名前空間stdを明示したり、using宣言を用いる必要があります。std::end;という記法が用いられている事がありますが、あれはまさに配列型へのADLが効かない問題に対する対処として用いられているのです。ちなみにusingディレクティブを用いても、基本はまずADLが優先されます。基底クラスの名前空間に対する探索ADLによる探索は基底クラスが宣言された名前空間にまで及びます。'std::move'?}このように、派生クラスが基底クラスとは異なる名前空間で宣言されているにもかかわらず、基底クラス依存のADLが発生してしまっていることが分かります。テンプレート引数による探索テンプレート引数として指定された型も探索の対象となります。'move'}テンプレート仮引数による探索ADLはテンプレート関数の仮引数型に対しても働きます。'move'}なお標準ライブラリやサードパーティ製のライブラリでは、このような仮引数に対するADLの発動を意図的に活用する場合もあります。ユーザ側で多重定義された関数(begin/end関数やswap関数等)を有効化するためです。ADLの問題と危険性ADLは名前空間の面倒な明示を省略できる点が便利ですが、ユーザ定義型との混在時や関数の多重定義時には余計な混乱や問題を引き起こすことがあります。"f(T)"}上記の例ではグローバル空間のf関数が呼ばれます。問題はない普通のコードです。名前空間nsの側で同名のf関数を定義してしまうと、main関数側ではADLによってns空間側のf関数が呼ばれるようになってしまいます。f(T)が呼ばれなくなってしまった}これは大変な問題です。名前空間ns内に関数を追加しただけで、元のコードが今までとは違う動作に変ってしまうわけですから。上記のような挙動の変化が、意図的な物であればまったく問題は無いですが、多くの開発シーンでは、上記のような変化が知らぬ間に、気づかない内に引き起こってしまう事があります。それはまさにADLの危険性であり、ADLの罠や落とし穴とも言うべきものです。ADLの回避(明示的回避)ADLの問題や危険性を回避するためには、ADLに依存しないコードを書く必要があります。名前空間を明示することでADLの発動を回避することができます。以下のコードでは名前空間stdを明示的に修飾することで、C++標準ライブラリ側のmove関数を明示的に呼び出しています。std::move(T)が呼ばれる}グローバル関数の明示グローバル関数の場合はスコープ解決演算子::のスコープ省略記法を用いることで、グローバル関数を明示的に呼び出すことができます。関数名を括弧で囲うことで、ADLの発動を回避することができます。f(T)が呼ばれる}ADLの回避(事前回避)ADL回避対象の関数をダミーの名前空間でラップすることで、ADLを回避することが可能です。ラップされた関数はusingディレクティブで本来の名前空間に展開します。bがADL対策後の名前空間です。関数呼び出しf(b)では、グローバル空間側の関数が呼び出されていることがわかります。T&)が呼ばれる}ちなみに、無名名前空間でADLを回避することもできます。