The rules are quite complicated, and I myself do not fully understand them, but let's see if we can make them head or tails (I think we can):
namespace nx { struct X {}; } namespace ns { auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X') } auto operator==(nx::X, nx::X) { return true; } auto global_foo() { return ns::foo(nx::X{}, nx::X{}); }
This was not found for a simple reason: operator== not declared before using it. With ADL, nothing yet. So far, so good. We understand that. Let fix it:
namespace nx { struct X {}; } auto operator==(nx::X, nx::X) { return true; } namespace ns { auto foo(nx::X x1, nx::X x2) { return x1 == x2; } } auto global_foo() { return ns::foo(nx::X{}, nx::X{}); }
It works? Yes, it does, it compiles and calls our operator== . This is the right decision? No! . Because if we add this:
namespace nx { struct X {}; } auto operator==(nx::X, nx::X) { return true; } // (1) namespace ns { template <class T> auto operator==(T, int) { return false; } // (2) auto foo(nx::X x1, nx::X x2) { return x1 == x2; } // error: no match for 'operator==' (operand types are 'nx::X' and 'nx::X') } auto global_foo() { return ns::foo(nx::X{}, nx::X{}); }
Then (2) in ns hides (1) in the global namespace, even if (1) matches better. This is called name hiding and - again - is not related to ADL in any way.
Worse:
namespace nx { struct X {}; } auto operator==(nx::X, nx::X) { return true; }
Compiled and silent (2) instead of our operator (1) .
For real-world context, think of namespace ns as the std and any statement declared inside std . And you have a situation in your post.
Correct solution:
namespace nx { struct X {}; auto operator==(nx::X, nx::X) { return true; }
Here, what happens is that ADL fires and brings (1) from nx , and now (1) considered next to (2) . But (1) more specialized than (2) , and therefore (1) correctly selected.
If you do not have control over namespace nx and you cannot add an operator there, then I can advise using instead of relying on operators. Instead of std::find use std::find_if with your own predicate (lambda), where you determine exactly which method / operator to call. And when I say "exactly," I mean exactly : ie ::operator==(x1, x2) (or any other namespace you declared) instead of x1 == x2 .
For more on this great article, you can read Herb Sutter Namespaces and the Principle of the Interface