I'm a little surprised that putting the variant's alternative types into a sub-namespace seems to break the operator==
:
#include <variant>
#include <iostream>
namespace NS {
namespace structs {
struct A {};
struct B {};
} // namespace structs
using V = std::variant<structs::A, structs::B>;
bool operator==(const V& lhs, const V& rhs)
{
return lhs.index() == rhs.index();
}
std::string test(const V& x, const V& y)
{
if (x == y) {
return "are equal";
} else {
return "are not equal";
}
}
namespace subNS {
std::string subtest(const V& x, const V& y)
{
if (x == y) {
return "are equal";
} else {
return "are not equal";
}
}
} // namespace subNS
} // namespace NS
int main() {
const auto u = NS::V{NS::structs::A{}};
const auto v = NS::V{NS::structs::A{}};
const auto w = NS::V{NS::structs::B{}};
// Why doesn't this work?
// It does work if A and B are defined in NS rather than NS::structs.
//std::cout << "u and v are " << (u == v ? "equal" : "not equal") << "\n";
//std::cout << "u and w are " << (u == w ? "equal" : "not equal") << "\n";
std::cout << "u and v " << NS::test(u, v) << "\n";
std::cout << "u and w " << NS::test(u, w) << "\n";
std::cout << "u and v " << NS::subNS::subtest(u, v) << "\n";
std::cout << "u and w " << NS::subNS::subtest(u, w) << "\n";
}
The only solution I have found is to define:
namespace std {
template<>
constexpr bool
operator==(const NS::V& lhs, const NS::V& rhs)
{
return lhs.index() == rhs.index();
}
} // namespace std
But this looks somewhat dubious, and appears forbidden from c++20: https://en.cppreference.com/w/cpp/language/extending_std#Function_templates_and_member_functions_of_templates
Any better ideas? Clearly I'm trying to avoid adding operator==
for each of the alternative types.
The reason is due to ADL. In particular, see [basic.lookup.argdep]/2: (emphasis mine)
In particular, the fact that the "using-declaration"
V
exists in::NS
does not affect argument-dependent lookup. The only namespaces that are considered are::std
, fromstd::variant
, and::NS::structs
, from the<structs::A, structs::B>
template arguments.NS::test
andNS::subNS::subtest
work because they do not need to rely on ADL to find the definition ofoperator==
contained inNS
.As already mentioned in the comments, the solution then is for
operator==
to be defined somewhere it will be found—in this case, that would be in::NS::structs
.