【问题标题】:Remove global implicit function - avoid ambiguous operator删除全局隐式函数 - 避免模棱两可的运算符
【发布时间】:2019-12-06 02:03:53
【问题描述】:

假设我有一个枚举类:

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

我现在想创建两个运算符

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

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

不幸的是,我得到一个编译器错误

错误 C2593:'operator ==' 不明确

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

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

为此。尝试用

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

不会导致该行出现编译错误,但之后我仍然会收到“模棱两可”错误。

关于如何进行这项工作的任何想法?

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

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

【问题讨论】:

  • 为什么你需要右值引用参数,我只是觉得它没有任何意义?您需要使用右值引用解决的实际和潜在问题是什么?用例是什么?您将如何(以及如何)使用比较运算符?
  • 为什么要“找出我的右值引用放在== 的哪一侧”?这可能是 X/Y 问题。
  • 我有一个变量,比如说 AntiParallel。与 BiParallel 相比,这应该返回 true。当变量持有 BiParallel 时,它应该仅在与 BiParallel 比较时为真。我想我会为它编写一个类以使我的意图更清晰,但我仍然想知道为什么会出现编译器错误。像这样:var == PType::BiParallel
  • void Foo(Bar bar)void Foo(Bar&& bar) 重载是不明确的,因为当参数是右值时,任何一个都是同样有效的选项。 void Foo(Bar const& bar)void Foo(Bar&& bar) 没有歧义。
  • 请解释你为什么不只实现bool operator==(PType, PType) 而没有别的?与operator== 相关的价值类别如何?我没有发现问题:godbolt.org/z/H5EAQ8

标签: c++ compiler-errors rvalue


【解决方案1】:

我已经为这个问题写了另一个答案,但 OP 指出我的答案不正确。再想一想,我认为OP想要做的事情根本不可能。

如 C++17 [over.built]/16 中所述,在重载解析期间,会生成带有签名的内置候选

bool operator==(PType, PType);

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

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

如果您确实使用内置候选者的确切签名声明了自己的 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 想要的似乎是不可能的。

【讨论】:

    猜你喜欢
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多