我听说C ++支持覆盖operator==
和operator!=
,因为在某些情况下a != b
可以实现为比!(a == b)
更高效。
我已经考虑过这个,无法想象这是真的。
对operator==
和operator!=
单独实施哪些有意义,性能方面或其他方面的示例?
答案 0 :(得分:4)
首先想到的示例是类似于SQL的NULL值的实现。在那里,比较两个对象 - 其中任何一个是NULL - 并不意味着它们是相等的。只有当两者都不为NULL时,返回相等才有意义。
答案 1 :(得分:3)
如果你希望a == b
在a != b
返回false时确实返回true的规则,那么确实没有理由有两个实现,除非你是希望以某种方式优化单个!
。 (这很少会产生影响,优化器最好完成。)
但是,C ++通常不会假设运算符重载遵守这样的规则。
例如,您可能还想到,您应该只需要重载operator <
,然后获取operator >
,运算符<=
,运算符>=
和运算符{{ 1}}免费。因为所有这些都可以用==
定义,如果你假设它返回一个bool并且该关系应该是一个部分顺序。
但是,在某些情况下,运算符也用于提供更复杂的语法和语义。如果这些&#34;身份&#34;例如,强制它会使表达模板之类的东西变得不可能。
C ++并没有强加任何&#34;身份&#34;在你身上无论好坏,你都可以为操作员提供你想要的任何意义。
所以,我认为你所听到的可能是一种误解。您拥有这种自由的原因不是为了提供更多的机会来提高效率,而是允许您在运营商使用自定义类时获得您想要的含义。
为了完整起见,这是我所谈论的一个例子。
operator <
在此代码中,运算符重载用于允许您构造函数对象以实现简单的布尔函数。函数构造过程完全在编译时发生,因此生成的代码非常有效。人们在更复杂的例子中使用它来在C ++中非常有效地进行某些类型的函数式编程。 namespace expression_builder {
struct arg {
bool operator()(bool input) const {
return input;
}
};
template <typename E>
struct negate {
E e;
bool operator()(bool input) const {
return !e(input);
}
};
template <typename E1, typename E2>
struct equals {
E1 e1;
E2 e2;
bool operator()(bool input) const {
return e1(input) == e2(input);
}
};
template <typename E1, typename E2>
struct not_equals {
E1 e1;
E2 e2;
bool operator()(bool input) const {
return e1(input) != e2(input);
}
};
// Operator overloads
template <typename T>
auto operator!(T t) -> negate<T> {
return {t};
}
template <typename T1, T2>
auto operator==(T1 t1, T2 t2) -> equals<T1, T2> {
return {t1, t2};
}
template <typename T1, T2>
auto operator!=(T1 t1, T2 t2) -> not_equals<T1, T2> {
return {t1, t2};
}
} // end namespace expression_builder
int main() {
using expression_builder::arg;
auto my_functor = (arg == (arg != (!arg)));
bool test1 = my_functor(true);
bool test2 = my_functor(false);
}
与operator ==
的实现方式截然不同,这一点至关重要。
答案 2 :(得分:0)
在非常简单的情况下,&#34;在不平等方面实现平等&#34; (反之亦然)成语就足够了。在x86上,cmp
instruction用于平等和不等式。请看以下示例:
struct Foo
{
bool operator==(const Foo& rhs)
{
return val == rhs.val;
}
bool operator!=(const Foo& rhs)
{
return val != rhs.val;
}
int val;
};
Foo a{20};
Foo b{40};
Foo c{20};
int main()
{
(void)(a == b);
(void)(a == c);
(void)(a != b);
(void)(a != c);
}
这将编译为相同的程序集,无sete
对setne
。人们可能会分裂头发并对分支预测,流水线,CPU缓存等做出模糊的断言。但它们实际上只是空洞的陈述。
在复杂的情况下,为operator==
和operator!=
提供不同的语义可能很诱人,但我不同意这个原则:
您违反了用户对这两个运营商彼此相反的期望。例如!(a == b)
或a != b
之间有什么区别吗?您很容易陷入其他语言的陷阱,如PHP和Javascript,其中相等是一种特殊的地狱类型。
隐藏运算符重载和迭代器后面的复杂对象会严重影响性能。人们使用这些功能时假设它们很便宜(在大多数情况下它们都是。)昂贵的#34;迭代器&#34;或者&#34;平等&#34;使得使用起来非常困难。
&#34;什么时候有意义&#34;不幸的是,商业需求而不是适当的设计更能解决问题。