什么时候对operator ==和operator!=进行单独的实现是有意义的?

时间:2016-06-11 02:38:14

标签: c++

我听说C ++支持覆盖operator==operator!=,因为在某些情况下a != b可以实现为比!(a == b)更高效。

我已经考虑过这个,无法想象这是真的。

operator==operator!=单独实施哪些有意义,性能方面或其他方面的示例?

3 个答案:

答案 0 :(得分:4)

首先想到的示例是类似于SQL的NULL值的实现。在那里,比较两个对象 - 其中任何一个是NULL - 并不意味着它们是相等的。只有当两者都不为NULL时,返回相等才有意义。

答案 1 :(得分:3)

如果你希望a == ba != 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);
}

这将编译为相同的程序集,无setesetne。人们可能会分裂头发并对分支预测,流水线,CPU缓存等做出模糊的断言。但它们实际上只是空洞的陈述。

在复杂的情况下,为operator==operator!=提供不同的语义可能很诱人,但我不同意这个原则:

  • 您违反了用户对这两个运营商彼此相反的期望。例如!(a == b)a != b之间有什么区别吗?您很容易陷入其他语言的陷阱,如PHP和Javascript,其中相等是一种特殊的地狱类型。

  • 隐藏运算符重载和迭代器后面的复杂对象会严重影响性能。人们使用这些功能时假设它们很便宜(在大多数情况下它们都是。)昂贵的#34;迭代器&#34;或者&#34;平等&#34;使得使用起来非常困难。

&#34;什么时候有意义&#34;不幸的是,商业需求而不是适当的设计更能解决问题。