ADL(実引数依存の名前探索) とは、関数のオーバーロード解決の際に引数の名前空間を探索するというものです。
驚くかもしれませんが、以下のコードは問題なくコンパイルできます。
namespace test
{
struct test_t {};
void func( test_t ); // bla bla
}
int main()
{
func( test::test_t() );
}
この悪名高いADLですが、実際はないと困る機能なのです。例えば以下のコード、ADLがなければ operator==
をかっちり書いてやらなければならなくなってしまいます。
std::string str1( "abc" );
std::string str2( "abcd" );
if( str1 == str2 ) { ... } // if ADL is disabled, no operator==() is found.
if( std::operator==( str1, str2 ) { ... } // ok
対策ですが、ADLは引数の名前空間を探索するものなのですから、関数と引数とで名前空間を分けてしまえばいいのです。
namespace test
{
namespace _test_t
{
struct test_t {};
}
using namespace _test_t;
void func( _test_t::test_t );
}
int main()
{
func( test::test_t() ); // func not found
}
名前空間で区切ったあとusingディレクティブで名前空間の壁を取っ払っていますが、ADLの探索条件から外れるのでこれで問題解決になります。
では、ADLが問題になりうるケースをいくつか紹介しておしまいにしようと思います。
1. 基底クラスの名前空間が探索の対象になる
namespace test
{
struct test_t {};
void func( test_t );
}
struct super_test_t : test::test_t {}; // public inheritance
int main()
{
func( super_test_t() ); // ok!
}
2. テンプレート引数の名前空間が探索の対象になる
namespace test
{
struct test_t {};
template < class Ty >
void func( Ty );
}
template < class Ty >
struct template_t {};
int main()
{
func( template_t < test::test_t >() ); // ok!
}
これらを組み合わさって複数の関数がADLによって見つかると、曖昧な呼び出しとして怒られます。
怒られない環境もあるようですね。恐ろしいことです。
namespace testA
{
struct test_t {};
template < class Ty >
void print( Ty ) { std::cout << "testA::print" << std::endl; }
}
namespace testB
{
template < class Ty >
struct template_t {};
template < class Ty >
void print( Ty ) { std::cout << "testB::print" << std::endl; }
}
int main()
{
print( testB::template_t < testA::test_t >() ); // ambiguous
}