删除全局隐式函数 - 避免模棱两可的运算符

Remove global implicit function - avoid ambiguous operator

假设我有一个枚举class:

enum class PType : int
{
   Parallel     = 0,
   AntiParallel = 1,
   BiParallel   = 2,
   NotParallel  = 3
};

我现在想创建两个运算符

bool operator==(PType lhs, PType&& rhs)
bool operator==(PType&& lhs, PType rhs)

这样我就可以找出我的右值引用放在 == 的哪一边。

不幸的是我遇到编译器错误

error C2593: 'operator ==' is ambiguous

在 VisualStudio 中,因为编译器会自动生成以下运算符:

bool operator==(const PType lhs, const PType rhs); 

为了它。尝试用

删除它
bool operator==(const PType lhs, const PType rhs) = delete;

不会导致该行出现编译错误,但之后我仍然会收到 "is ambiguous" 错误。

关于如何使这项工作有任何想法吗?

编辑: 我知道如何使用常规 class 来做到这一点,但我仍然想弄清楚为什么编译器会产生错误。我知道代码很粗略,风格不好,可能会导致许多人提到的错误。 无论如何,我认为这种类型的东西可以工作,所以我尝试了并且我认为如果我只能让“= delete”工作,它仍然可以工作。 所以现在我出于纯粹的学术兴趣问这个问题,因为我想了解更多关于编译器的信息,以及为什么当我删除函数时编译器没有报告错误,但几行之后又抱怨说据称删除的函数被认为是重载候选者。

之所以要区分右值引用的位置,是因为我知道变量在 == 的哪一侧以及可以在哪一侧找到像 PType::BiParallel 这样的比较值。这样做的原因是,与 PType::BiParallel 相比,包含 Parallel 或 AntiParallel 的变量应该 return 为真,因为我只想知道我们是否有某种并行性。另一方面,如果变量包含 BiParallel 并与 PType::Parallel 或 PType::AntiParallel 进行比较,则两个比较都应该是错误的,因为您不能说哪个是正确的。 同样,我知道这是不好的风格,但我发现编译器接受“= delete”的原因在学术上很有趣。

这个问题我已经写了另一个答案,但是OP指出我的答案不正确。仔细想想,我认为OP的做法是不可能的。

如 C++17 [over.built]/16 中所述,在重载解析期间,将生成一个带有签名

的内置候选者
bool operator==(PType, PType);

如果选择内置候选项,则将应用 == 的内置语义。

现在,可以定义您自己的 operator==,但除非您声明一个 具有与内置候选完全相同的签名,否则内置- in candidate 仍然会生成。因此,在重载解决时,内置候选者要么获胜(由于完全匹配),要么与某些用户声明的重载相关联,导致歧义。

如果您使用内置候选者的确切签名声明自己的 operator==,则不会生成内置候选者 ([over.match.oper]/(3.3.4) ).如果您愿意,可以删除它:

bool operator==(PType, PType) = delete;

但是,删除的函数仍然参与重载决议(除非它是默认的移动构造函数或移动赋值运算符)。因此,这并不能解决问题:使用额外的重载,例如

bool operator==(PType&&, PType);

存在歧义的可能性,只有在实际知道参数类型时才会出现这种歧义。这就是为什么在您实际尝试使用 ==.

之前不会出现编译错误的原因

您真正想要的是某种方法来防止内置候选函数或具有相同签名的另一个函数完全不被重载决策考虑。如果可以这样做,则可以强制根据重载集中的值类别选择重载,如下所示:

bool operator==(const PType& lhs, const PType& rhs);
bool operator==(const PType& lhs, PType&& rhs);
bool operator==(PType&& lhs, const PType& rhs);
bool operator==(PType&& lhs, PType&& rhs);

但据我所知,没有办法做到这一点,所以 OP 想要的似乎是不可能的。