The following code doesn't compile, as the comparison operator is not found.
#include <vector>
#include <iostream>
#include <string>
namespace Cool {
struct Person {
std::string name;
};
}
bool operator==(const Cool::Person& p1, const Cool::Person& p2) {
return p1.name == p2.name;
}
int main(int, char *[])
{
std::vector<Cool::Person> a{ {"test"} };
std::vector<Cool::Person> b{ {"test"} };
bool ok = a == b;
std::cout << ok << std::endl;
}
After some experiments I found out, that the following perfectly compiles:
#include <vector>
#include <iostream>
#include <string>
namespace Cool {
struct Person {
std::string name;
};
bool operator==(const Person& p1, const Person& p2) {
return p1.name == p2.name;
}
}
int main(int, char *[])
{
std::vector<Cool::Person> a{ {"test"} };
std::vector<Cool::Person> b{ {"test"} };
bool ok = a == b;
std::cout << ok << std::endl;
}
Can someone explain the rationale behind this behavior?
This is called ADL, or Argument dependent lookup.
For operators, the compiler will not only search in the current namespace for a suitable function, but in the namespaces of the arguments too.
For example:
int main() {
int arr[3] = {};
std::vector<int> vec(3);
auto b_vec = begin(vec); // std::begin
auto b_arr = begin(arr); // Error!
}
When calling begin
with vec, it will search in the std
namespace since std::vector
is in that namespace. For a raw array, the function is not found since it has no namespace associated to that type.
One way to turn off ADL is to simply qualify the function:
// calls the std:: one, not the boost:: one
std::begin(some_boost_container);
This is also how friend function works:
struct test {
friend void find_me(int) {}
};
find_me(3); // uh? no matching function?
Even though the function is perfectly fine, it cannot be found.
It need the name of the class inside it's argument so ADL kicks in and find it in the class scope:
struct test {
friend void find_me(test const&) {}
};
find_me(test{}); // works!
So... for operators? why does it work?
because calling a user defined operator is roughly equivalent to that:
// arg1 == arg2;
operator==(arg1, arg2);
Since the function name is not qualified, ADL kicks in. Then find the operator in the right namespace and can also find friend functions.
This both allow a nice syntax and also allow generic function to call function inside your namespace.
So why the vector cannot find the one in the global namespace?
This is because how ADL works. Inside a namespace with a function with a name in it, ADL will only search inside the namespace of the argument. But if one cannot be found, it will fallback to normal lookup.
namespace Cool {
struct Person {
std::string name;
};
}
bool cant_you_find_me(Cool::Person const& p);
namespace test {
void cant_you_find_me();
template<typename T>
void test_template(T const& p) {
cant_you_find_me(p); // ADL?
}
}
In this example, ADL will search for a function cant_you_find_me
, but if one cannot be found through ADL, the global namespace will not be considered since normal lookup will find the closest one instead: the one taking no arguments.
This is what happens with the std
namespace. It has many operator==
defined in it. If ADL does not find a suitable one, the global namespace will not be considered, but the ones in the std
instead.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With