Подтвердить что ты не робот

Определить общий оператор сравнения

Мне пришла в голову идея определить общий оператор сравнения, который работал бы с любым типом, для удовольствия.

#include <cstring>
#include <iostream>

class A
{
    public:
        A(int id) : id(id) {}

    private:
        int id;
};

template <class T>
inline bool operator==(const T& a, const T& b)
{
    return memcmp(&a, &b, sizeof(a)) == 0; // implementation is unimportant (can fail because of padding)
}

int main()
{
    std::cout << (A(10) == A(10)) << std::endl; // 1
    std::cout << (A(10) == A(15)) << std::endl; // 0
}

Я думаю, что это может быть полезно обойти отсутствие оператора сравнения по умолчанию в С++.

Это ужасная идея? Интересно, может ли это сделать что-нибудь в некоторых случаях?

4b9b3361

Ответ 1

Это действительно страшная идея.

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

Даже в случае, когда отсутствующий оператор равенства был надзором со стороны разработчика, любая реализация "всякий раз", которую вы придумали, вряд ли сделает что-то разумное.

Итак, чтобы заключить: Не делайте этого! Ошибки компиляции лучше, чем ошибки времени выполнения; вместо преждевременного добавления, безусловно, сломанного "решения", скрывающего фактическую проблему, добавьте актуальные решения по мере возникновения ошибок времени компиляции.


Во-первых, решение, с которым вы столкнулись, терпит неудачу для типов с заполнением, типами с перегруженным унарным operator& и любым типом, который имеет некоторый указатель или ссылочный элемент; или даже типы с любым членом или базой любой из вышеупомянутых категорий. Так что за тонну вещей.

Ответ 2

Возьмем совершенно нормальный класс, скажем String. Он реализован, как вы думаете, с char*, который указывает на буфер new[] 'ed.

Теперь сравните два из них. Очевидно, String("abc")==String("abc"). Однако ваша реализация не проходит этот тест, поскольку два указателя отличаются друг от друга.

Равенство определяется семантикой класса, а не битами непосредственно внутри объекта.

Ответ 3

Да, это ужасная идея:
в случае неинициализированного указателя:
Вот пример неудачного (так что этот код имеет два разных выхода):

#include <cstring>
#include <iostream>

class A {
public:
  A(int id) : id(id) {}

private:
  int id;
  A* a;
};

template <class T> inline bool operator==(const T &a, const T &b) {
  return memcmp(&a, &b, sizeof(a)) == 0;
}

int main() {
  std::cout << (A(10) == A(10)) << std::endl; // 1
  std::cout << (A(10) == A(15)) << std::endl; // 0
}

выход:

0
0

и шансы на два одинаковых значения начального содержимого ОЗУ для двух указателей крайне маловероятны, тогда другой вывод:

1
0

Ответ 4

В качестве незначительного аспекта, самый прекрасный способ, которым я знаю, писать операции равенства для классов с большим количеством членов, использует эту идею (для этого кода требуется С++ 14):

#include <tuple>

struct foo
{
    int x    = 1;
    double y = 42.0;
    char z   = 'z';

    auto
    members() const
    {
        return std::tie(x, y, z);
    }
};

inline bool
operator==(const foo& lhs, const foo& rhs)
{
    return lhs.members() == rhs.members();
}

int
main()
{
    foo f1;
    foo f2;

    return f1 == f2;
}

Код в компиляторе