覆盖标准::变体::运算符==



我有点惊讶将变体的替代类型放入子命名空间似乎打破了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==

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

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

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

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

NS::testNS::subNS::subtest工作,因为它们不需要依赖 ADL 来查找NS中包含的operator==的定义。

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

我认为这现在已经得到了@Eljay的回答:

[放置] 运算符 == 在 NS::structs 中。

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

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

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

最新更新