运算符 >= 重载的奇怪行为
-
23-08-2019 - |
题
我在 C++ 中的运算符重载中遇到了奇怪的行为。我有一个类,我需要检查它的内容是否大于或等于 long double。我重载了 >= 运算符来进行此检查,我的声明如下:
bool MyClass::operator>=(long double value) const;
我不得不说,我的班级还有一个强制转换为长双精度运算符,它仅在某些条件下才无例外地工作。现在,当我使用此运算符时,编译器抱怨运算符 >= 的使用不明确,替代方案是:
- 矿。
- 内置的
operator>=(long double, int)
.
现在,我如何强制程序使用我的运算符?
解决方案
2015年更新: 或者,如果您想使用 (double)obj
语法代替 obj.to_double()
语法,制作转换函数 explicit
通过在其前面加上该关键字作为前缀。然后您需要显式强制转换才能触发转换。就我个人而言,我更喜欢 .to_double
语法,除非转换为 bool
因为在这种情况下,转换被使用 if(obj)
即使是 explicit
, ,这比 if(obj.to_bool())
在我看来。
删除转换运算符。就会一路带来麻烦。有一个类似的功能
to_double()
或者类似的返回双精度值并显式调用该函数以获取双精度值。
对于手头的问题,有这样的问题:
obj >= 10
考虑一下这个表达。内置运算符使用转换运算符 long double() 通过用户定义的类型转换序列来匹配第一个参数。但是您的函数通过从 int 到 long double (整数到浮点转换)的标准转换序列来匹配第二个参数。当有两个参数的转换,但不是至少有一个参数可以更好地转换,而其余参数对于一次调用没有转换得更差时,它总是不明确的。在您的情况下,内置函数与第二个参数匹配得更好,但第一个参数匹配得更差,但是您的函数与第一个参数匹配得更好,但第二个参数匹配得更差。
这很令人困惑,所以这里有一些例子(从 char 到 int 的转换称为促销,这比从 char 到 int 以外的转换更好,这称为转换):
void f(int, int);
void f(long, long);
f('a', 'a');
调用第一个版本。因为第一个的所有参数都可以更好地转换。同样,以下仍将调用第一个:
void f(int, long);
void f(long, long);
f('a', 'a');
因为第一个可以转换的比较好,第二个不可以转换的比较差。但下面的是 模糊的:
void f(char, long);
void f(int, char);
f('a', 'a'); // ambiguous
这个案例就更有趣了。第一个版本通过完全匹配接受第一个参数。第二个版本通过精确匹配接受第二个参数。但两个版本至少都没有同样好地接受他们的其他论点。第一个版本需要对其第二个参数进行转换,而第二个版本需要对其参数进行提升。因此,即使升级比转换更好,对第二个版本的调用也会失败。
这和你上面的情况非常相似。即使标准转换序列(从 int/float/double 转换为 long double)是 更好的 与用户定义的转换序列(从 MyClass 转换为 long double)相比,您的运算符版本未被选择,因为您的其他参数(long double)需要从参数进行转换,这比内置运算符对该参数所需的转换更糟糕(完美搭配)。
重载解析在 C++ 中是一件复杂的事情,因此人们不可能记住其中所有微妙的规则。但获得粗略的计划是很有可能的。我希望它对你有帮助。
其他提示
通过提供到 a 的隐式转换 double
你实际上是在说,我的课程相当于 double
因此,您不应该真正介意内置运算符 >= for double
使用s。如果你 做 关心,那么你的课程确实不“等同于” double
并且您应该考虑不提供 隐含的 转换为 double
, ,而是提供一个 明确的 GetAsDouble 或 ConvertToDouble 成员函数。
您目前有歧义的原因是对于表达式 t >= d
在哪里 t
是你的类的一个实例并且 d
是一个双精度数,编译器总是必须提供左侧或右侧的转换,因此表达式确实是不明确的。任何一个 t
的 operator double
被调用并且内置运算符 >= for double
使用 s,或者 d 必须提升为 a long double
并且使用了您的成员运算符 >= 。
编辑,您已经更新了您的问题以表明您的转换是 long double 并且您的比较是针对 int 的。在这种情况下,最后一段应为:
您目前有歧义的原因是对于表达式 t >= d
在哪里 t
是你的类的一个实例并且 d
是一个 int
, ,编译器总是必须提供左侧或右侧的转换,因此表达式确实是不明确的。任何一个 t
的 operator long double
被调用并且内置运算符 >= for long double
和 int
使用,或者 d 必须提升为 a long double
并且使用了您的成员运算符 >= 。
我假设你正在与文字进行比较 int
, ,而不是一个 long double
:
MyClass o;
if (o >= 42)
{
// ...
}
如果是这种情况,两种选择都一样好/复杂。
使用您的 operator long double()
:
MyClass::operator long double()
- 内置
operator>=(long double, int)
使用您的 MyClass::operator>=(long double)
:
- 内置转换
int
到long double
MyClass::operator>=(long double)
你有 long double
在声明中。尝试将其更改为 double
.
将运算符重载与自定义转换结合使用可能会让类的用户感到非常困惑。问问自己,此类的用户会 预计 它将自身转换为双精度型,或者与双精度型相当?拥有 .greaterThan(double) 函数是否可以实现相同的目标而不会让用户感到惊讶?
我想你总是可以在比较之前显式地将你的对象转换为 double ,以避免歧义。但如果我是你,我会重新考虑上面的方法,并专注于编写直观且行为方式毫不奇怪的代码,而不是花哨的类型转换和运算符重载。
(受到 FQA 精彩的启发 关于运算符重载的抱怨)
- 内置运算符>=(long double, int)。
看起来你已经定义了:
bool class::operator>=(long double value) { return value >= classValue; }
而你缺少的是:
bool class::operator>=(double value) { return value >= classValue; }
bool class::operator>=(int value) { return value >= classValue; }
所以编译器无法决定采用哪种方式转换。(这是模棱两可的。)
也许模板化函数(或方法)会有帮助?
留意以下情况: a>=b 调用不同的方法 b>=a.