我在C ++中运算符重载时遇到了奇怪的行为。我有一个班级,我需要检查它的内容是否大于或等于一个长双。我重载了> =运算符来进行此检查,我的声明如下:
bool MyClass::operator>=(long double value) const;
我必须说我的班级也有一个强制转换为双精度运算符,只有在某些条件下才有效。 现在,当我使用这个运算符时,编译器会抱怨运算符> =的含糊不清,其替代方法是:
operator>=(long double, int)
。现在,如何强制程序使用我的运算符?
答案 0 :(得分:3)
2015更新:或者,如果您希望使用(double)obj
语法而不是obj.to_double()
语法保持转换能力,请通过添加前缀来制作转化函数explicit
它与该关键字。您需要一个显式的强制转换然后才能触发转换。就个人而言,我更喜欢.to_double
语法,除非转换为bool
,因为在这种情况下转换由if(obj)
使用,即使它是explicit
,这是在我看来,比if(obj.to_bool())
更具可读性。
删除转化运算符。它会一路引起麻烦。有像
这样的功能to_double()
或类似的返回double值并显式调用该函数以获得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 ++中是一个复杂的问题,因此人们不可能记住其中的所有细微规则。但是获得粗略的计划是非常有可能的。我希望它可以帮助你。
答案 1 :(得分:3)
通过提供对double
的隐式转换,您实际上是在说明,我的类等同于double
,因此您不应该介意内置运算符> = for使用double
s。如果您做关注,那么您的课程与“{1}}”并不“等同”,您应该考虑不向{{1}提供隐式转换},而是提供显式 GetAsDouble或ConvertToDouble成员函数。
目前你有歧义的原因是,对于表达式double
,其中double
是你的类的一个实例而t >= d
是一个double,编译器总是必须提供左手侧或右手侧的转换,因此表达式确实不明确。调用t
的{{1}}并使用内置运算符> = d
s,或者必须将d提升为t
和您的成员运算符> =已使用。
编辑,你已经更新了你的问题,建议你的转换加倍,你的比较是针对int的。在这种情况下,最后一段应为:
目前您有歧义的原因是,对于表达式operator double
,其中double
是您班级的一个实例而long double
是t >= d
,编译器总是必须提供左侧或右侧的转换,因此表达式确实不明确。调用t
的{{1}}并使用内置运算符> = d
和int
,或者必须将d提升为t
并且使用了您的成员运算符> =。
答案 2 :(得分:1)
我假设您要与文字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)
答案 3 :(得分:0)
宣言中有long double
。尝试将其更改为double
。
答案 4 :(得分:0)
您使用运算符重载与自定义转换相结合可能会让您的班级用户感到非常困惑。问问自己,这个类的用户期望它将自己转换为double,还是与double相媲美?不会有.greaterThan(双重)功能实现相同的目标,但不会让用户感到惊讶吗?
我猜你总是可以在比较前明确地将你的对象强制转换为双倍,以避免歧义。但如果我是你,我会重新考虑上面的方法,并专注于编写直观的代码,并且表现得毫不奇怪,而不是花哨的类型转换和运算符重载。
(灵感来自FQA的精彩rant about operator overloading)
答案 5 :(得分:0)
看起来你已定义:
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 不同的方法的情况。