为什么在很多C++标准库代码中将不等式测试为(!(a == b))?

Ahm*_*war 142 c++

这是C++标准库remove代码中的代码.为什么不平等测试if (!(*first == val))而不是if (*first != val)

 template <class ForwardIterator, class T>
      ForwardIterator remove (ForwardIterator first, ForwardIterator last, const T& val)
 {
     ForwardIterator result = first;
     while (first!=last) {
         if (!(*first == val)) {
             *result = *first;
             ++result;
         }
         ++first;
     }
     return result;
 }
Run Code Online (Sandbox Code Playgroud)

Tom*_*ner 144

因为这意味着对T的唯一要求是实现一个operator==.你可以要求T有一个,operator!=但这里的一般想法是你应该尽可能减轻模板用户的负担,而其他模板确实需要operator==.

  • @AidanGomez无论好坏,你都可以让操作员超负荷做任何你想做的事情.它没有意义或一致. (20认同)
  • template <class T> inline bool operator!= <T a,T b> {return!(a == b); } (13认同)
  • `x!= y`未定义为与`!(x == y)`相同.如果这些运算符返回嵌入式DSL的解析树怎么办? (10认同)
  • 是否存在编译器无法交换=的所有实例的任何情况!来!(==)?为什么这不是编译器的默认操作? (8认同)
  • @Joshua如果试图使用SFINAE来检测是否支持`!=`(将错误地返回true,即使不支持`operator ==`!),那会破坏性.我也担心它会导致`!=`的某些用法变得含糊不清. (7认同)

Luk*_*řík 36

STL中的大多数功能仅适用于operator<operator==.这要求用户仅实现这两个操作符(或者有时至少其中一个).例如std::set使用operator<(更确切地说std::less,operator<默认情况下调用)而不operator>管理排序.remove您的示例中的模板是类似的情况 - 它仅使用operator==而不是operator!=因此operator!=不需要定义.

  • 函数不直接使用`operator <`而是使用`std :: less`,而`std :: less`又默认为`operator <`. (2认同)
  • @ChristianHackl,`std :: equal_to`和`std :: less`用作默认模板参数,其中比较器作为参数.`operator ==`和`operator <`直接用于需要类型的地方,分别满足相等和严格的弱排序,例如迭代器和随机访问迭代器. (2认同)

Chr*_*ckl 28

这是来自C++标准库的代码删除代码.

错误.这不是 C++标准库remove的代码.它是C++标准库函数的一种可能的内部实现remove.C++标准没有规定实际代码; 它预示着功能原型和所需行为.

换句话说:从严格的语言角度来看,您看到的代码不存在.它可能来自编译器标准库实现附带的一些头文件.请注意,C++标准甚至不需要存在这些头文件.文件只是编译器实现者满足类似行的要求#include <algorithm>(即制作std::remove和其他可用功能)的便捷方式.

为什么不平等测试if (!(*first == val))而不是if (*first != val)

因为只有operator==功能需要.

当涉及到自定义类型的运算符重载时,该语言允许您执行各种奇怪的事情.您可以很好地创建一个具有重载operator==但没有重载的类operator!=.或者更糟糕的是:你可能会超载,operator!=但却完全没有相关的东西.

考虑这个例子:

#include <algorithm>
#include <vector>

struct Example
{
    int i;

    Example() : i(0) {}

    bool operator==(Example const& other) const
    {
        return i == other.i;
    }

    bool operator!=(Example const& other) const
    {
        return i == 5; // weird, but nothing stops you
                       // from doing so
    }

};

int main()
{
  std::vector<Example> v(10);
  // ...
  auto it = std::remove(v.begin(), v.end(), Example());
  // ...
}
Run Code Online (Sandbox Code Playgroud)

如果std::remove使用operator!=,那么结果将完全不同.

  • "从严格的语言角度来看,你所看到的代码并不存在." - 当一个观点说某些东西不存在,但你实际上在看那个东西时,那么观点是错误的.事实上标准并没有说代码不存在,它只是不说它确实存在:-) (18认同)
  • @supercat:IEEE-754确实使`==`和`!=`表现得一致,尽管我一直认为当至少一个操作数是'NaN`时,所有六个关系都应该评估为'false`. (2认同)

Ric*_*ges 15

这里有一些好的答案.我只想添加一点注释.

像所有好的库一样,标准库的设计考虑了(至少)两个非常重要的原则:

  1. 将最少的责任放在您可以逃脱的图书馆用户身上.部分原因是在使用界面时给予他们最少的工作量.(就像定义尽可能少的操作员一样).它的另一部分与它们并不奇怪或要求它们检查错误代码(因此保持接口一致并<stdexcept>在出现问题时抛出异常).

  2. 消除所有逻辑冗余.所有的比较都可以仅仅从中推断出来operator<,那么为什么要求用户定义其他的?例如:

    (a> b)相当于(b <a)

    (a> = b)相当于!(a <b)

    (a == b)相当于!((a <b)||(b <a))

    等等.

    当然,在本文中,人们可能会问为什么unordered_map要求operator==(至少在默认情况下)而不是operator<.答案是在哈希表中,我们需要的唯一比较是一个相等的.因此,它要求它们定义相等运算符在逻辑上一致(即对库用户更有意义).要求一个operator<会让人感到困惑,因为它并不是很明显你为什么需要它.

  • 关于你的第二点:有些类型可以在逻辑上进行比较,即使没有逻辑顺序,或者建立一个订单是非常人为的.例如,红色是红色,红色不是绿色,但红色本身小于绿色? (10认同)

小智 8

这个EqualityComparable概念需要operator==定义.

因此,任何声称使用满足类型的函数EqualityComparable 都不能依赖于该operator!=类型对象的存在.(除非有其他要求暗示存在operator!=).