为什么C ++不会自动使用operator ==而不是operator!=

时间:2012-10-17 18:18:58

标签: c++

我理解C ++无法自动为某个类定义operator==,但是当!(a == b)不可用时,为什么a != b无法使用operator!=但{ {1}}是吗?

我知道operator==虽然我在今天之前没有听说过它。

4 个答案:

答案 0 :(得分:6)

因为operator==并不一定意味着与operator!=相反。

我无法想到operator==不代表!operator!=的任何实例,但它们是独立的运算符。关于C ++最令人解放,有时甚至是最令人沮丧的事情之一就是C ++对如何编写代码应用了一组最小的限制。如果您的实例operator==operator!=不相反,那么您应该能够在C ++中表达它。事实上,你可以。

你在C ++中利用坏处。你可能会认为这是一个“坏”的集合。

请记住,在绝大多数情况下,根据operator!=正确实施operator==是微不足道的。

bool Gizmo::operator!=(const Gizmo& rhs) const
{
  return !operator==(rhs);
}

答案 1 :(得分:5)

C ++作为一种语言不提供您没有明确要求的功能。我知道这个哲学对于默认构造函数等有点破坏,但这是Stroustrup很早就做出的一个设计决策 - 你没有为你不使用的东西支付。因此,编译器不会自动生成您不需要的内容。

1993年初提到了Bjarne的电子邮件链on the ACCU web site,其中提到了这一点。如果我没记错的话,它也在D& E中;我没有方便参考的副本。

答案 2 :(得分:3)

不允许该语言执行您想要的操作。 operator==operator!=是两个不同的运营商。我想不出!(x==y)x!=y会产生不同结果的示例,但请考虑operator<= vs operator>。为什么你需要这两个?可以将x<=y写为!(x>y),对吗?错。

#include<iostream>

int main () {
   double y = 0.0;
   double x = y/y;

   std::cout << " (x <= y) -> " << (x <= y) << "\n";
   std::cout << "!(x >  y) -> " << !(x > y) << "\n";
}

答案 3 :(得分:1)

C ++的第一个版本(更正后称为C ++ 03)引入了默认构造函数,复制构造函数,复制赋值运算符和析构函数的自动定义,以便能够在C ++中编译C语言。

它已被证明,因为它可能不是最好的选择,但许多为析构函数提供自定义定义的人忘记定义复制构造函数和赋值运算符,最终在他们的腿上弄得一团糟。

隐藏的方法,比如隐藏的执行路径,似乎让开发人员感到困惑。我想我们都被咬了。


然而,

C ++ 11对按需默认方法有一个非常聪明的机制:

class Test { Test() = default; }; // a rather useless class...

所以,借鉴C ++ 03自动生成构造函数&amp; co,我不赞成引入这种自动生成,但肯定会支持:

bool operator!=(Test const&, Test const&) = default;

(范围operator==,显然)

同样地:

bool operator>(Test const&, Test const&) = default;
bool operator<=(Test const&, Test const&) = default;
bool operator>=(Test const&, Test const&) = default;

(范围operator<,显然)


然而,我们可能会问真正的问题:为什么不提供一个更通用的方法?

operator==通常没有拙劣,但我看到了无数operator<的破坏实施。显然,尊重弱势秩序并不像看起来那么容易(*)。不过,如果你认为元组,它只是两个元组的字典比较,真的!

(*)您有==<的实现不匹配(即通常!(a < b) and !(b < a)&lt; =&gt; a == b),但是元组确实解决了这个问题!

实际上:

std::tuple<int, std::string const&> to_tuple(Test const&);

通常可用于生成初始运算符:

template <typename T>
auto operator==(T const& left, T const& right) -> decltype(to_tuple(left), bool{}) {
    return to_tuple(left) == to_tuple(right);
}

template <typename T>
auto operator<(T const& left, T const& right) -> delctype(to_tuple(right), bool{}) {
    return to_tuple(left) < to_tuple(right);
}

那么,陷阱是什么?好吧,ADL。当这些模板存在于与您实现to_tuple的类不同的命名空间中时,事情就会崩溃,因为它们不会被ADL自动拾取(同样的原因using std::swap如此常见......)。

那么,那么,如果bool operator==(Test const&, Test const&) = default;在范围内,我们可以说to_tuple(Test const&)应该做正确的事(tm)。它甚至都不会疯狂。不太多。

但是,看看我离原始提案有多远?想象一下委员会的决定最终是什么......


同时?

嗯,就个人而言,我实施:

#define MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, Op_)                       \
    inline bool operator Op_ (Type_ const& left, Type_ const& right) {  \
        return to_tuple(left) Op_ to_tuple(right);                      \
    }                                                                   \

#define MY_DEFINE_TUPLE_EQUAL(Type_)                                    \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, ==)                            \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, !=)

#define MY_DEFINE_TUPLE_COMP(Type_)                                     \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_,  <)                            \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_,  >)                            \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, <=)                            \
    MY_DEFINE_TUPLE_OPERATOR_IMPL(Type_, >=)

然后:

class Test;

std::tuple<int, std::string const&> to_tuple(Test const&); // or boost::tuple

MY_DEFINE_TUPLE_EQUAL(Test);
MY_DEFINE_TUPLE_COMP(Test);

它与ADL一起使用,它会在to_tuple周围生成内联代码(可能是内联本身也可能不内联),它会生成正确且一致的==<实现,并且输入的次数少于所有6种方法的= default

甚至为编译器错误消息留下源位置!

那么......为什么语言会进一步复杂化呢?