using declarationは、その名の通り宣言なので、ある名前空間で他の名前空間の関数をusing宣言すると、その関数はADLによって呼ばれる可能性が生じる。
#include <iostream>
void print(void){}
namespace ns1{
template<typename T>
void print(T const& obj){
std::cout << obj <<std::endl;
}
}
namespace ns2{
struct foo{
int bar = 0;
friend std::ostream& operator<<(std::ostream& o, foo const& obj){
return o << obj.bar;
}
};
using ::ns1::print;
}
int main() {
print(ns2::foo{}); //ns1::printが呼ばれる。
return 0;
}
逆に、他の名前空間に属する関数をADLで呼び出せるようにしたい場合、これを利用できる。例えば、std::rel_ops名前空間内の関数テンプレートは、これを応用するのが最善の活用方法だと思われる(もっとも、std::rel_opsはC++2aでdeprecatedになる)。
#include <iostream>
#include <utility>
namespace my{
namespace _impl{
struct foo{
int bar = 0;
bool operator==(foo other)const{
return bar == other.bar;
}
bool operator<(foo other)const{
return bar < other.bar;
}
};
using std::rel_ops::operator!=;
using std::rel_ops::operator<=;
using std::rel_ops::operator>;
using std::rel_ops::operator>=;
}
typedef _impl::foo foo;
}
int main() {
my::foo foo1{100}, foo2{1000};
std::cout << std::boolalpha;
std::cout << (foo1 != foo2) <<std::endl;
std::cout << (foo1 <= foo2) <<std::endl;
std::cout << (foo1 > foo2) <<std::endl;
std::cout << (foo1 >= foo2) <<std::endl;
return 0;
}