ADLは邪悪らしい

TwitterでADLについて語っていたのでそのまとめ。

  • ADLは基本的に必須の機能。
  • でもテンプレートと複数の名前空間が絡むと意図しないLookupを起こすことがある。

参考: http://d.hatena.ne.jp/uskz/20060526/p1

  • 意図しないADLを回避するにはネストされた名前空間とusingを使う。

参考: http://d.hatena.ne.jp/uskz/20060712/p1

  • Boostも同じことをして意図しないADLを回避している箇所がある。

意図しないADLとその回避のサンプル

#include <iostream>

#define AVOID_ADL 0

namespace y {
	class piyo { };

#if AVOID_ADL
	namespace detail {
		template <typename T>
		void f(T &) {
			std::cout << "y::f" << std::endl;
		}
	}
	using namespace detail;
#else
		template <typename T>
		void f(T &) {
			std::cout << "y::f" << std::endl;
		}
#endif
}

namespace x {
	template <typename T>
	void f(const T &) {
		std::cout << "x::f" << std::endl;
	}
	
	template <typename T>
	class hoge { };
}

int main() {
	x::hoge<y::piyo> h;
	f(h); // AVOID_ADL==0だとy::fが呼ばれるが、x::hogeの作者は当然x::fを呼んで欲しいはず
	      // AVOID_ADL==1だとx::fが呼ばれる(y::piyoの作者はy::fをうっかり呼んで欲しくないことを明確にしている)
}

ネストされた名前空間の名前について

今回はnamespace detailとしていますが、namespace yの中での衝突の危険を減らすためにも、関連するクラスごとに独立した名前空間にしたほうがいいかもしれません。
(この場合はnamespace avoid_adl_piyo等)

日本語で読めるC++の書籍では、ADLは善いものであるという論調がほとんどで、このような意図しないLookupについて詳しく言及して注意を呼びかけているのは(日本では)一部のブログだけで、何がどう邪悪なのか理解するのに少々苦労しました。
ADL簡易解説 - Faith and Brave - C++で遊ぼう本の虫: n2930: Range-based for loopについて なども読んでおくといいかもしれません。