覆盖 std::variant::operator==

Overriding std::variant::operator==

我有点惊讶,将变体的替代类型放入子命名空间似乎会破坏 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";
}

我找到的唯一解决方案是定义:

namespace std {

template<>
constexpr bool
operator==(const NS::V& lhs, const NS::V& rhs)
{
    return lhs.index() == rhs.index();
}

} // namespace std

但这看起来有点可疑,并且似乎被 c++20 禁止:https://en.cppreference.com/w/cpp/language/extending_std#Function_templates_and_member_functions_of_templates

有什么更好的主意吗?显然,我试图避免为每个替代类型添加 operator==

我认为@Eljay 已经回答了这个问题:

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

相关子句是 3. a) 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)

原因是ADL。特别是,请参阅 [basic.lookup.argdep]/2:(强调我的)

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.

特别是,::NS 中存在“using-declaration”V 这一事实不会影响参数相关查找。考虑的 名称空间是 ::std,来自 std::variant,和 ::NS::structs,来自 <structs::A, structs::B> 模板参数。

NS::testNS::subNS::subtest 工作是因为它们不需要依赖 ADL 来查找 NS.

中包含的 operator== 的定义

正如评论中已经提到的那样,解决方案是将 operator== 定义在可以找到的地方——在这种情况下,就是 ::NS::structs.