Я большой поклонник того, чтобы компилятор делал для вас как можно больше работы. При написании простого класса компилятор может дать вам «бесплатно» следующее:
- Конструктор по умолчанию (пустой)
- Конструктор копирования
- Деструктор
- Оператор присваивания (
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
{ }
Есть ли для этого веская причина? Почему выполнение сравнения между членами будет проблемой? Очевидно, что если класс выделяет память, вам следует быть осторожным, но для простого класса наверняка компилятор может сделать это за вас?
==
, так же как и автоматическое назначение по умолчанию (=
) при определенных условиях. (Аргумент о указателях противоречив, потому что логика применяется как для, так=
и для==
, а не только для второго).Ответы:
Компилятор не знает, нужно ли вам сравнение указателей или глубокое (внутреннее) сравнение.
Безопаснее просто не реализовывать это и позволить программисту делать это самостоятельно. Тогда они могут сделать все предположения, которые им нравятся.
источник
operator=
) обычно работают в том же контексте, операторы сравнения - то есть, есть надежда , что после выполненияa = b
,a == b
верно. Компилятору определенно имеет смысл предоставить значение по умолчанию,operator==
используя ту же семантику агрегированных значений, что и дляoperator=
. Я подозреваю, что paercebal на самом деле здесь прав, посколькуoperator=
(и копия ctor) предназначены исключительно для совместимости с C, и они не хотели ухудшать ситуацию.Аргумент, что если компилятор может предоставить конструктор копирования по умолчанию, он должен быть в состоянии обеспечить аналогичное значение по умолчанию,
operator==()
имеет определенный смысл. Я думаю, что причина решения не предоставлять сгенерированный компилятором по умолчанию для этого оператора может быть угадана из того, что сказал Страуструп о конструкторе копирования по умолчанию в «Проектировании и развитии C ++» (Раздел 11.4.1 - Управление копированием) :Таким образом, вместо «почему в C ++ нет значения по умолчанию
operator==()
?», Вопрос должен был звучать так: «Почему в C ++ есть конструктор присваивания и копирования по умолчанию?», Ответ на который заключается в том, что эти элементы неохотно включались в Stroustrup для обратной совместимости с C (вероятно, причина большинства бородавок C ++, но также, вероятно, основная причина популярности C ++).Для моих собственных целей в моей IDE сниппет, который я использую для новых классов, содержит объявления для частного оператора присваивания и конструктора копирования, так что при создании нового класса я не получаю операции присваивания и копирования по умолчанию - мне нужно явно удалить объявление из этих операций из
private:
раздела, если я хочу, чтобы компилятор мог генерировать их для меня.источник
Foo(const Foo&) = delete; // no copy constructor
иFoo& Foo=(const Foo&) = delete; // no assignment operator
struct
, но мне бы хотелось, чтобы онclass
вел себя по-другому (и разумно). В процессе, это также дало бы более значимую разницу между доступом по умолчаниюstruct
иclass
рядом с ним.operator==
. На данный момент это просто синтаксический сахар для некоторого кода. Если вы боитесь, что таким образом программист может пропустить какой-либо указатель среди полей класса, вы можете добавить условие, что он может работать только с примитивными типами и объектами, которые сами имеют операторы равенства. Однако нет никаких оснований полностью это запрещать.Даже в C ++ 20 компилятор все равно не будет генерировать
operator==
для вас неявноНо вы получите возможность явного дефолта,
==
начиная с C ++ 20 :Значение по умолчанию выполняется для каждого
==
элемента==
(так же, как конструктор копирования по умолчанию выполняет создание элемента для каждого элемента). Новые правила также обеспечивают ожидаемые отношения между==
и!=
. Например, с объявлением выше, я могу написать как:Эта особенность (дефолт
operator==
и симметрия между==
и!=
) происходит от одного предложения, которое было частью более широкой языковой функцииoperator<=>
.источник
= default
, для вещи, которая не создана по умолчанию, верно? Для меня это звучит как оксюморон («явное значение по умолчанию»).ИМХО, нет "веской" причины. Причина, по которой так много людей согласны с этим дизайнерским решением, заключается в том, что они не научились овладевать силой семантики, основанной на ценностях. Людям нужно написать много пользовательских конструкторов копирования, операторов сравнения и деструкторов, потому что они используют необработанные указатели в своей реализации.
При использовании соответствующих интеллектуальных указателей (например, std :: shared_ptr) конструктор копирования по умолчанию обычно в порядке, и очевидная реализация гипотетического оператора сравнения по умолчанию будет такой же.
источник
Он ответил, что C ++ не сделал ==, потому что C не сделал, и вот почему C предоставляет только default =, но не == на первом месте. C хотел, чтобы все было просто: C реализовано = memcpy; однако == не может быть реализован memcmp из-за заполнения. Поскольку padding не инициализируется, memcmp говорит, что они разные, хотя они одинаковые. Та же проблема существует для пустого класса: memcmp говорит, что они разные, потому что размер пустых классов не равен нулю. Из вышесказанного видно, что реализация == более сложна, чем реализация = в C. Некоторые примеры кода, касающиеся этого. Ваша поправка приветствуется, если я ошибаюсь.
источник
operator=
- это будет работать только для типов POD, но C ++ также предоставляет значениеoperator=
по умолчанию для не POD-типов.В этом видео Алексей Степанов, создатель STL, решает этот вопрос примерно в 13:00. Подводя итог, наблюдая за развитием C ++, он утверждает, что:
Затем он говорит, что в (далеком) будущем == и ! = Будут сгенерированы неявно.
источник
C ++ 20 позволяет легко реализовать оператор сравнения по умолчанию.
Пример из cppreference.com :
источник
Point
качестве примера для упорядочения работы, так как нет никакого разумного способа по умолчанию для того , две точкиx
иy
координаты ...std::set
чтобы убедиться, что все точки уникальны иstd::set
используютсяoperator<
только.auto
: для этого случая мы можем всегда предполагать, что это будетstd::strong_ordering
из#include <compare>
?std::common_comparison_category_t
, который для этого класса становится ordering по умолчанию (std::strong_ordering
).Невозможно определить значение по умолчанию
==
, но вы можете определить значение!=
по умолчанию, с помощью==
которого вы обычно должны определять себя. Для этого вы должны сделать следующие вещи:Вы можете увидеть http://www.cplusplus.com/reference/std/utility/rel_ops/ для деталей.
Кроме того, если вы определите
operator<
, операторы для <=,>,> = могут быть выведены из него при использованииstd::rel_ops
.Но вы должны быть осторожны при использовании,
std::rel_ops
потому что операторы сравнения могут быть выведены для типов, для которых вы не ожидаете.Более предпочтительным способом вывести связанный оператор из базового является использование boost :: operator .
Подход, используемый в boost, лучше, потому что он определяет использование оператора для класса, который вам нужен, а не для всех классов в области видимости.
Вы также можете генерировать «+» из «+ =», - из «- =» и т. Д. (См. Полный список здесь )
источник
!=
после написания==
оператора. Или я сделал, но ему не хваталоconst
. Пришлось написать самому тоже и все было хорошо.rel_ops
C ++ 20 есть причина, по которой она устарела: она не работает , по крайней мере, не везде, и, конечно, не всегда. Нет надежного способа получитьsort_decreasing()
компиляцию. С другой стороны, Boost.Operators работает и всегда работал.В C ++ 0x
естьпредложение функций по умолчанию, поэтому вы могли бы сказать, чтоdefault operator==;
мы узнали, что это помогает сделать эти вещи явными.источник
operator==
. Что жаль.Концептуально не легко определить равенство. Даже для данных POD можно утверждать, что даже если поля одинаковы, но это другой объект (по другому адресу), он не обязательно равен. Это на самом деле зависит от использования оператора. К сожалению, ваш компилятор не экстрасенс и не может вывести это.
Помимо этого, стандартные функции являются отличным способом выстрелить себе в ногу. Описанные вами значения по умолчанию в основном сохраняют совместимость со структурами POD. Однако они приводят к более чем достаточному хаосу, когда разработчики забывают о них или семантике реализаций по умолчанию.
источник
int
созданный посредством копирования ctor из другого, равен тому, из которого он был создан; единственная логическая вещь, которую нужно сделать для одногоstruct
из двухint
полей, - это работать точно так же.+
оператором можно привести почти такой же аргумент, что он неассоциативен для чисел с плавающей точкой; то есть(x + y) + z
! =x + (y + z)
, из-за способа округления FP. (Возможно, это гораздо более серьезная проблема, чем то,==
что она имеет место для обычных числовых значений.) Вы можете предложить добавить новый оператор сложения, который работает для всех числовых типов (даже целых) и почти точно такой же, как+
и ассоциативный ( как - то). Но тогда вы добавите к языку раздувание и путаницу, не помогая такому количеству людей.Это может не быть проблемой функционально, но с точки зрения производительности сравнение по элементам по умолчанию может оказаться более неоптимальным, чем назначение / копирование по умолчанию между членами. В отличие от порядка назначения, порядок сравнения влияет на производительность, потому что первый неравный элемент подразумевает, что остальные могут быть пропущены. Поэтому, если есть некоторые члены, которые обычно равны, вы хотите сравнить их последними, и компилятор не знает, какие члены с большей вероятностью будут равны.
Рассмотрим этот пример, где
verboseDescription
длинная строка выбрана из сравнительно небольшого набора возможных описаний погоды.(Конечно, компилятор будет иметь право игнорировать порядок сравнений, если он признает, что у них нет побочных эффектов, но, вероятно, он все равно будет получать свою очередь из исходного кода, где у него нет собственной лучшей информации.)
источник
Просто чтобы ответы на этот вопрос оставались полными с течением времени: начиная с C ++ 20 он может быть автоматически сгенерирован командой
auto operator<=>(const foo&) const = default;
Он сгенерирует все операторы: ==,! =, <, <=,> И> =, подробнее см. Https://en.cppreference.com/w/cpp/language/default_comparisons .
Из-за взгляда оператора
<=>
он называется оператором космического корабля. Также см. Зачем нам нужен оператор космического корабля <=> в C ++? ,РЕДАКТИРОВАТЬ: также в C ++ 11 довольно аккуратная замена для этого доступна с
std::tie
см. Https://en.cppreference.com/w/cpp/utility/tuple/tie для полного примера кода сbool operator<(…)
. Интересная часть, измененная для работы с==
:std::tie
работает со всеми операторами сравнения и полностью оптимизируется компилятором.источник
Я согласен, для классов типа POD компилятор может сделать это за вас. Однако то, что вы можете считать простым, компилятор может ошибаться. Так что лучше позволить программисту сделать это.
У меня был случай POD, когда два поля были уникальными, поэтому сравнение никогда не считалось бы верным. Однако сравнение, которое мне было нужно, сравнивалось только с полезной нагрузкой - то, что компилятор никогда не поймет или не сможет понять самостоятельно.
Кроме того - они не занимают много времени, чтобы написать, они ?!
источник