Оператор No == найден при сравнении структур в С++

Сравнивая два экземпляра следующей структуры, я получаю сообщение об ошибке:

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' (или там не является приемлемым преобразованием)

Почему?

Ответ 1

В С++, struct не имеют оператора сравнения, сгенерированного по умолчанию. Вы должны написать свой собственный:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}

Ответ 2

Как говорили другие люди, вам нужно реализовать функцию сравнения самостоятельно.

Существует предложенный способ попросить компилятор сгенерировать очевидную/наивную (?) Реализацию: см. Здесь.

Может показаться немного бесполезным C++, чтобы не стандартизировать это, но часто структуры/классы имеют некоторые элементы данных, которые необходимо исключить из сравнения (например, счетчики, кэшированные результаты, емкость контейнера, код последней успешной операции/код ошибки, курсоры), а также решения о множестве вещей, включая, но не ограничиваясь:

  • какие поля сравнивать первыми, например, сравнение конкретного члена int может очень быстро устранить 99% неравных объектов, в то время как элемент map<string,string> часто может иметь одинаковые записи и сравнительно дорого сравнивать - если значения загружаются во время выполнения программист может понимать, что компилятор не может
  • в сравнении строк: чувствительность к регистру, эквивалентность пробелов и разделителей, выход из соглашений...
  • точность при сравнении чисел с плавающей точкой
  • следует ли считать значения с плавающей точкой NaN равными
  • сравнение указателей или указателей на данные (и, если последнее, как узнать, относятся ли указатели к массивам и сколько объектов/байтов нуждается в сравнении)
  • имеет ли значение порядок при сравнении несортированных контейнеров (например, vector, list) и, если да, можно ли отсортировать их на месте перед сравнением или использовать дополнительную память для сортировки временных данных каждый раз, когда выполняется сравнение
  • сколько элементов массива в настоящее время содержат допустимые значения, которые следует сравнить (есть ли где-нибудь размер или часовой?)
  • какой член union сравнить
  • нормализация: например, типы даты могут разрешать выходной день-месяц или месяц-год, или рациональный/дробный объект может иметь 6/8, а другой имеет 3/4, что по причинам производительности они корректируют лениво с отдельным шагом нормализации; Возможно, вам придется решить, следует ли инициировать нормализацию перед сравнением
  • что делать, если слабые указатели недействительны
  • как работать с членами и базами, которые сами не реализуют 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-членная версия здесь)

Обсуждение специфики вашего MyStruct1

Существуют последствия для выбора предоставления автономного 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 мучительно созданы, чтобы их просто выбросили! Для типов со значениями с плавающей запятой сравнение видимых представлений означает, что количество отображаемых цифр определяет допуск, в пределах которого почти равные значения рассматриваются как равные во время сравнения.

Ответ 3

Вам нужно явно указать operator == для MyStruct1.

struct MyStruct1 {
  bool operator == (const MyStruct1 &rhs) const
  { /* your logic for comparision between "*this" and "rhs" */ }
};

Теперь сравнение == законно для 2 таких объектов.

Ответ 4

Сравнение не работает над структурами на C или С++. Сравните по полям.

Ответ 5

По умолчанию в структурах нет оператора ==. Вы должны будете написать свою собственную реализацию:

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }

Ответ 6

Из коробки оператор == работает только для примитивов. Чтобы заставить ваш код работать, вам нужно перегрузить оператор == для вашей структуры.

Ответ 7

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

Ответ 8

Начиная с С++ 20, должна быть возможность добавить полный набор операторов сравнения по умолчанию (==, <= и т.д.) В класс, объявив оператор трехстороннего сравнения по умолчанию (оператор "космический корабль"), например этот:

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

При наличии совместимого компилятора С++ 20 добавление этой строки в MyStruct1 и MyStruct2 может быть достаточным для сопоставления на равенство при условии, что определение MyStruct2 совместимо.