0

将变体的替代类型放入子命名空间似乎会破坏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==为每种替代类型添加。

4

2 回答 2

1

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

对于函数调用中的每个参数类型 T,有一组零个或多个关联的名称空间和一组零个或多个关联的实体(名称空间除外)需要考虑。命名空间和实体的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)决定。用于指定类型的 Typedef 名称和 using-declarations 不构成该集合。命名空间和实体集通过以下方式确定:

  • ...
  • 如果 T 是一个类类型(包括联合),它的关联实体是:类本身;它所属的类别(如有的话);及其直接和间接基类。其关联的命名空间是其关联实体的最内层封闭的命名空间。此外,如果 T 是类模板特化,则其关联的命名空间和实体还包括: 为模板类型参数(不包括模板模板参数)提供的模板实参类型相关的命名空间和实体;用作模板模板参数的模板;任何模板模板参数都是其成员的命名空间;以及用作模板模板参数的任何成员模板是其成员的类。

特别是,V存在“使用声明”这一事实::NS不会影响依赖于参数的查找。唯一考虑的命名空间是模板参数中的::std, fromstd::variant::NS::structs, 。<structs::A, structs::B>

NS::test并且NS::subNS::subtest工作,因为它们不需要依赖 ADL 来查找operator==包含在NS.

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

于 2020-12-08T21:45:12.467 回答
0

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

[将] operator== 放在 NS::structs 中。

相关条款是 3. a) 在https://en.cppreference.com/w/cpp/language/adl

  1. 对于类型为类模板特化的参数,除了类规则之外,还会检查以下类型并将其关联的类和命名空间添加到集合中

    a) 为类型模板参数提供的所有模板实参的类型(跳过非类型模板参数和跳过模板模板参数)

于 2020-12-08T17:56:42.643 回答