Ответ 1
В С++, struct
не имеют оператора сравнения, сгенерированного по умолчанию. Вы должны написать свой собственный:
bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return /* your comparison code goes here */
}
Сравнивая два экземпляра следующей структуры, я получаю сообщение об ошибке:
struct MyStruct1 {
Position(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
my_struct_2(_my_struct_2),
an_int(_an_int)
{}
std::string toString() const;
MyStruct2 my_struct_2;
int an_int;
};
Ошибка:
ошибка C2678: двоичный '==': нет оператора найденный, который принимает левый операнд типа 'myproj:: MyStruct1' (или там не является приемлемым преобразованием)
Почему?
В С++, struct
не имеют оператора сравнения, сгенерированного по умолчанию. Вы должны написать свой собственный:
bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return /* your comparison code goes here */
}
Как говорили другие люди, вам нужно реализовать функцию сравнения самостоятельно.
Существует предложенный способ попросить компилятор сгенерировать очевидную/наивную (?) Реализацию: см. Здесь.
Может показаться немного бесполезным C++, чтобы не стандартизировать это, но часто структуры/классы имеют некоторые элементы данных, которые необходимо исключить из сравнения (например, счетчики, кэшированные результаты, емкость контейнера, код последней успешной операции/код ошибки, курсоры), а также решения о множестве вещей, включая, но не ограничиваясь:
int
может очень быстро устранить 99% неравных объектов, в то время как элемент map<string,string>
часто может иметь одинаковые записи и сравнительно дорого сравнивать - если значения загружаются во время выполнения программист может понимать, что компилятор не можетvector
, list
) и, если да, можно ли отсортировать их на месте перед сравнением или использовать дополнительную память для сортировки временных данных каждый раз, когда выполняется сравнениеunion
сравнитьoperator==
(но могут иметь compare()
или operator<
или str()
или геттеры...)Таким образом, приятно иметь ошибку, пока вы явно не подумали о том, что сравнение должно означать для вашей конкретной структуры, вместо того, чтобы позволить ей скомпилироваться, но не дать значимый результат во время выполнения.
Все это говорит о том, что было бы хорошо, если бы C++ позволил вам сказать bool operator==() const = default;
когда вы решили, что "наивный" тест "член за членом" ==
был в порядке. То же самое для !=
Учитывая несколько членов/баз, реализации "default" <
, <=
, >
и >=
кажутся безнадежными, хотя каскадирование на основе порядка объявления возможно, но очень маловероятно, что требуется, учитывая противоречивые императивы для упорядочения членов (основания являются обязательно перед членами, группировка по доступности, строительство/уничтожение перед зависимым использованием). Чтобы быть более широко полезным, C++ потребуется новая система аннотаций для элементов данных/баз данных, которая будет направлять выбор - это было бы замечательно иметь в Стандарте, хотя и идеально в сочетании с генерацией пользовательского кода на основе AST... Я ожидаю, что это произойдет однажды.
Вполне вероятно, что разумной и эффективной реализацией будет:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
Обратите внимание, что для MyStruct2
тоже нужен operator==
.
Последствия этой реализации и ее альтернативы обсуждаются в разделе "Обсуждение особенностей вашего MyStruct1" ниже.
Легко использовать операторы сравнения std::tuple
для сравнения ваших собственных экземпляров класса - просто используйте std::tie
для создания кортежей ссылок на поля в нужном порядке сравнения. Обобщая мой пример отсюда:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) ==
std::tie(rhs.my_struct2, rhs.an_int);
}
inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) <
std::tie(rhs.my_struct2, rhs.an_int);
}
// ...etc...
Когда вы "владеете" (то есть можете редактировать фактор с корпоративными и сторонними библиотеками) класса, который хотите сравнить, и особенно с готовностью C++ 14 вывести тип возвращаемого значения функции из оператора return
, часто лучше добавить "привязать" функцию-член к классу, который вы хотите сравнить:
auto tie() const { return std::tie(my_struct1, an_int); }
Тогда приведенные выше сравнения упрощают до:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
Если вам нужен более полный набор операторов сравнения, я предлагаю увеличить операторы (ищите less_than_comparable
). Если это по какой-то причине не подходит, вам может понравиться или не понравиться идея поддержки макросов (онлайн):
#define TIED_OP(STRUCT, OP, GET_FIELDS) \
inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
{ \
return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
}
#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
TIED_OP(STRUCT, ==, GET_FIELDS) \
TIED_OP(STRUCT, !=, GET_FIELDS) \
TIED_OP(STRUCT, <, GET_FIELDS) \
TIED_OP(STRUCT, <=, GET_FIELDS) \
TIED_OP(STRUCT, >=, GET_FIELDS) \
TIED_OP(STRUCT, >, GET_FIELDS)
... что может быть использовано а-ля...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(C++ 14-членная версия здесь)
Существуют последствия для выбора предоставления автономного operator==()
члена operator==()
...
Автономная реализация
Вам нужно принять интересное решение. Поскольку ваш класс может быть неявно создан из MyStruct2
, автономная/не bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
членом bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
функция будет поддерживать...
my_MyStruct2 == my_MyStruct1
... сначала создав временный MyStruct1
из my_myStruct2
, а затем выполнив сравнение. Это определенно оставит MyStruct1::an_int
установленным в значение параметра конструктора по умолчанию -1
. В зависимости от того, включаете ли вы сравнение an_int
в реализацию вашего operator==
, MyStruct1
может сравнивать или не сравнивать его с MyStruct2
который сам сравнивается равным MyStruct1
my_struct_2
! Кроме того, создание временного MyStruct1
может быть очень неэффективной операцией, так как оно включает в себя копирование существующего члена my_struct2
во временный, только чтобы отбросить его после сравнения. (Конечно, вы можете предотвратить эту неявную конструкцию MyStruct1
для сравнения, сделав этот конструктор explicit
или удалив значение по умолчанию для an_int
.)
Реализация участников
Если вы хотите избежать неявного конструирования MyStruct1
из MyStruct2
, сделайте оператор сравнения функцией-членом:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
Обратите внимание, что ключевое слово const
- необходимое только для реализации члена - сообщает компилятору, что сравниваемые объекты не изменяют их, поэтому могут быть разрешены для const
объектов.
Иногда самый простой способ получить желаемое сравнение может быть...
return lhs.to_string() == rhs.to_string();
... что часто очень дорого - эти string
мучительно созданы, чтобы их просто выбросили! Для типов со значениями с плавающей запятой сравнение видимых представлений означает, что количество отображаемых цифр определяет допуск, в пределах которого почти равные значения рассматриваются как равные во время сравнения.
Вам нужно явно указать operator ==
для MyStruct1
.
struct MyStruct1 {
bool operator == (const MyStruct1 &rhs) const
{ /* your logic for comparision between "*this" and "rhs" */ }
};
Теперь сравнение == законно для 2 таких объектов.
Сравнение не работает над структурами на C или С++. Сравните по полям.
По умолчанию в структурах нет оператора ==
. Вы должны будете написать свою собственную реализацию:
bool MyStruct1::operator==(const MyStruct1 &other) const {
... // Compare the values, and return a bool result.
}
Из коробки оператор == работает только для примитивов. Чтобы заставить ваш код работать, вам нужно перегрузить оператор == для вашей структуры.
Потому что вы не пишете оператор сравнения для своей структуры. Компилятор не генерирует его для вас, поэтому, если вы хотите сравнить, вы должны написать его самостоятельно.
Начиная с С++ 20, должна быть возможность добавить полный набор операторов сравнения по умолчанию (==
, <=
и т.д.) В класс, объявив оператор трехстороннего сравнения по умолчанию (оператор "космический корабль"), например этот:
struct Point {
int x;
int y;
auto operator<=>(const Point&) const = default;
};
При наличии совместимого компилятора С++ 20 добавление этой строки в MyStruct1 и MyStruct2 может быть достаточным для сопоставления на равенство при условии, что определение MyStruct2 совместимо.