Определить общий оператор сравнения
Мне пришла в голову идея определить общий оператор сравнения, который работал бы с любым типом, для удовольствия.
#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
}
Я думаю, что это может быть полезно обойти отсутствие оператора сравнения по умолчанию в С++.
Это ужасная идея? Интересно, может ли это сделать что-нибудь в некоторых случаях?
Ответы
Ответ 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;
}
Код в компиляторе