简体   繁体   中英

Overriding std::variant::operator==

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)

For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated entities (other than namespaces) to be considered. The sets of namespaces and entities are determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and entities are determined in the following way:

  • ...
  • If T is a class type (including unions), its associated entities are: the class itself ; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated entities. Furthermore, if T is a class template specialization, its associated namespaces and entities also include: the namespaces and entities associated with the types of the template arguments provided for template type parameters (excluding template template parameters); the templates used as template template arguments; the namespaces of which any template template arguments are members; and the classes of which any member templates used as template template arguments are members.

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 , from std::variant , and ::NS::structs , from the <structs::A, structs::B> template arguments.

NS::test and NS::subNS::subtest work because they do not need to rely on ADL to find the definition of operator== contained in NS .

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 .

I think this has now been answered by @Eljay:

[Put] the operator== in NS::structs.

The pertinent clause is 3. a) in https://en.cppreference.com/w/cpp/language/adl :

  1. For arguments whose type is a class template specialization, in addition to the class rules, the following types are examined and their associated classes and namespaces are added to the set

    a) The types of all template arguments provided for type template parameters (skipping non-type template parameters and skipping template template parameters)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM