Почему компиляторы C ++ не определяют operator == и operator! =? - PullRequest
273 голосов
/ 20 октября 2008

Я большой поклонник того, чтобы компилятор делал для вас как можно больше работы. При написании простого класса компилятор может дать вам «бесплатно» следующее:

  • Конструктор по умолчанию (пустой)
  • Конструктор копирования
  • Деструктор
  • Оператор присваивания (operator=)

Но это не может дать вам никаких операторов сравнения - таких как operator== или operator!=. Например:

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

Есть ли для этого веская причина? Почему выполнение сравнения между членами будет проблемой? Очевидно, что если класс выделяет память, вам следует быть осторожным, но для простого класса компилятор наверняка может сделать это за вас?

Ответы [ 12 ]

288 голосов
/ 20 октября 2008

Аргумент, что если компилятор может предоставить конструктор копирования по умолчанию, он должен быть в состоянии предоставить аналогичное значение по умолчанию operator==(), имеет определенный смысл. Я думаю, что причина решения не предоставлять сгенерированный компилятором по умолчанию для этого оператора может быть угадана тем, что сказал Страуструп о конструкторе копирования по умолчанию в «Проектировании и развитии C ++» (Раздел 11.4.1 - Управление копированием) :

Лично я считаю это неудачным что операции копирования определяются по умолчанию, и я запрещаю копирование объекты многих из моих классов. Тем не менее, C ++ унаследовал его по умолчанию присваивание и копирование конструкторов из С, и они часто используются.

Таким образом, вместо «почему в C ++ нет значения по умолчанию operator==()?», Вопрос должен был звучать так: «Почему в C ++ есть конструктор присваивания и копирования по умолчанию?», С ответом, что эти элементы неохотно включались Stroustrup для обратной совместимости с C (вероятно, причина большинства бородавок C ++, но также, вероятно, основная причина популярности C ++).

Для моих собственных целей в моей IDE фрагмент, который я использую для новых классов, содержит объявления для частного оператора присваивания и конструктора копирования, так что когда я генерирую новый класс, у меня нет операций присваивания и копирования по умолчанию - мне нужно явно удалите объявление этих операций из раздела private:, если я хочу, чтобы компилятор мог сгенерировать их для меня.

70 голосов
/ 20 октября 2008

Компилятор не знает, требуется ли вам сравнение указателей или глубокое (внутреннее) сравнение.

Безопаснее просто не реализовывать это и позволить программисту делать это самостоятельно. Тогда они могут сделать все предположения, которые им нравятся.

65 голосов
/ 08 января 2015

Даже в C ++ 20 компилятор по-прежнему неявно генерирует operator== для вас

struct foo
{
    std::string str;
    int n;
};

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ill-formed

Но вы получите способность явно по умолчанию ==:

struct foo
{
    std::string str;
    int n;

    // either member form
    bool operator==(foo const&) const = default;
    // ... or friend form
    friend bool operator==(foo const&, foo const&) = default;
};

По умолчанию == выполняет для элемента == (так же, как конструктор копирования по умолчанию выполняет построение копии для каждого элемента). Новые правила также обеспечивают ожидаемые отношения между == и !=. Например, с объявлением выше, я могу написать оба:

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ok!
assert(foo{"Anton", 1} != foo{"Anton", 2}); // ok!

Эта особенность (по умолчанию operator== и симметрия между == и !=) взята из одного предложения , которое было частью расширенной языковой функции operator<=>.

42 голосов
/ 03 апреля 2009

ИМХО, "хорошей" причины нет. Причина, по которой так много людей согласны с этим дизайнерским решением, заключается в том, что они не научились овладевать силой семантики, основанной на ценностях. Людям нужно написать много пользовательских конструкторов копирования, операторов сравнения и деструкторов, потому что они используют необработанные указатели в своей реализации.

При использовании соответствующих интеллектуальных указателей (например, std :: shared_ptr) конструктор копирования по умолчанию обычно в порядке, и очевидная реализация гипотетического оператора сравнения по умолчанию будет такой же.

36 голосов
/ 07 декабря 2011

Он ответил, что C ++ не сделал ==, потому что C не сделал, и вот почему C предоставляет только default =, но не == на первом месте. C хотел сделать это простым: C реализовано = по memcpy; однако, == не может быть реализован memcmp из-за заполнения. Поскольку padding не инициализируется, memcmp говорит, что они разные, хотя они одинаковые. Та же проблема существует для пустого класса: memcmp говорит, что они разные, потому что размер пустых классов не равен нулю. Из вышесказанного видно, что реализация == сложнее, чем реализация = в C. Некоторый код пример относительно этого. Ваша поправка приветствуется, если я ошибаюсь.

24 голосов
/ 28 апреля 2014

В этом видео Алексей Степанов, создатель STL, отвечает на этот вопрос примерно в 13:00. Подводя итог, наблюдая за развитием C ++, он утверждает, что:

  • К сожалению, == и! = не объявлены неявно (и Бьярне соглашается с ним). На правильном языке эти вещи должны быть готовы для вас (он далее рекомендует, что вы не сможете определить ! = , который нарушает семантику == )
  • Причина, по которой дело обстоит так, имеет свои корни (как и многие проблемы C ++) в C. Там оператор присваивания неявно определяется с помощью побитовое присваивание , но это не будет работать для == . Более подробное объяснение можно найти в этой статье от Bjarne Stroustrup.
  • В последующем вопросе Почему тогда не было сравнения членов по элементам он говорит удивительная вещь : C был своего рода доморощенным языком, и парень, реализующий эти вещи потому что Ричи сказал ему, что это трудно реализовать!

Затем он говорит, что в (далеком) будущем == и ! = будут сгенерированы неявно.

15 голосов
/ 20 октября 2008

Невозможно определить значение по умолчанию ==, но вы можете определить значение по умолчанию != через ==, которое вы обычно должны определять сами. Для этого вы должны сделать следующие вещи:

#include <utility>
using namespace std::rel_ops;
...

class FooClass
{
public:
  bool operator== (const FooClass& other) const {
  // ...
  }
};

Подробнее см. http://www.cplusplus.com/reference/std/utility/rel_ops/.

Кроме того, если вы определите operator< , операторы для <=,>,> = могут быть выведены из него при использовании std::rel_ops.

Но вы должны быть осторожны, когда используете std::rel_ops, потому что операторы сравнения могут быть выведены для типов, для которых вы не ожидаете.

Более предпочтительный способ вывести связанный оператор из базового - использовать boost :: operator .

Подход, используемый в boost, лучше, потому что он определяет использование оператора для класса, который вам нужен, а не для всех классов в области.

Вы также можете сгенерировать «+» из «+ =», - из «- =» и т. Д. (Полный список здесь )

13 голосов
/ 15 мая 2018

C ++ 20 позволяет легко реализовать оператор сравнения по умолчанию.

Пример из cppreference.com :

class Point {
    int x;
    int y;
public:
    auto operator<=>(const Point&) const = default;
    // ... non-comparison functions ...
};

// compiler implicitly declares operator== and all four relational operators work
Point pt1, pt2;
if (pt1 == pt2) { /*...*/ } // ok, calls implicit Point::operator==
std::set<Point> s; // ok
s.insert(pt1); // ok
if (pt1 <= pt2) { /*...*/ } // ok, makes only a single call to Point::operator<=>
10 голосов
/ 20 октября 2008

C ++ 0x имеет предложение для функций по умолчанию, так что вы можете сказать default operator==; Мы узнали, что это помогает сделать эти вещи явными.

5 голосов
/ 20 октября 2008

Концептуально не легко определить равенство. Даже для данных POD можно утверждать, что даже если поля одинаковы, но это другой объект (по другому адресу), он не обязательно равен. Это на самом деле зависит от использования оператора. К сожалению, ваш компилятор не экстрасенс и не может вывести это.

Кроме того, функции по умолчанию являются отличным способом выстрелить себе в ногу. Описанные вами значения по умолчанию в основном сохраняют совместимость со структурами POD. Однако они вызывают более чем достаточный хаос, когда разработчики забывают о них или семантике реализаций по умолчанию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...