Как сравнивать объекты типов POD
В этом примере:
#include <iostream>
#include <cstring>
struct A
{
int a;
bool b;
};
bool foo( const A a1, const A a2 )
{
return ( 0 == std::memcmp( &a1, &a2, sizeof( A ) ) );
}
int main()
{
A a1 = A();
a1.a = 5;a1.b = true;
A a2 = A();
a2.a = 5;a2.b = true;
std::cout<<std::boolalpha << foo( a1, a2 ) << std::endl;
}
будет производить false
из-за заполнения.
У меня нет доступа к функции foo
, и я не могу изменить способ сравнения.
Предполагая, что a bool
занимает 1 байт (это верно в моей системе), если я изменил struct A
на это:
struct A
{
int a;
bool b;
char dummy[3];
};
тогда он отлично работает в моей системе (выход true
).
Есть ли что-нибудь еще, что я мог бы сделать, чтобы исправить эту проблему (получить вывод true
)?
Ответы
Ответ 1
Первый не работает из-за заполнения в структуре. Прокладка имеет разные битовые шаблоны для обоих объектов.
Если вы используете memset
для установки всех битов в объекте перед его использованием, то он будет работать:
A a1;
std::memset(&a1, 0, sizeof(A));
a1.a = 5;a1.b = true;
A a2;
std::memset(&a2, 0, sizeof(A));
a2.a = 5;a2.b = true;
Онлайн-демонстрации:
Кстати, вы также можете писать operator<
, operator==
и т.д. для POD.
Ответ 2
Начиная с С++ 11 мы можем использовать кортежи для простого сравнения POD (кортежи используют лексикографическое сравнение для операторов >
, <
, >=
и <=
, подробнее об этом: https://en.cppreference.com/w/cpp/utility/tuple/operator_cmp):
#include <iostream>
#include <tuple>
struct Point {
int x;
int y;
int z;
};
auto pointToTuple(const Point& p) {
return std::make_tuple(p.x, p.y, p.z);
}
bool operator==(const Point& lhs, const Point& rhs ) {
return pointToTuple(lhs) == pointToTuple(rhs);
}
bool operator<(const Point& lhs, const Point& rhs ) {
return pointToTuple(lhs) < pointToTuple(rhs);
}
int main()
{
Point a{1, 2, 3};
Point b{1, 2, 3};
Point c{2, 2, 2};
std::cout << (pointToTuple(a) == pointToTuple(b) ? "true" : "false") << "\n"; //true
std::cout << (pointToTuple(a) == pointToTuple(c) ? "true" : "false") << "\n"; //false
std::cout << (a == b ? "true" : "false") << "\n"; //true
std::cout << (a == c ? "true" : "false") << "\n"; //false
std::cout << (a < b ? "true" : "false") << "\n"; //false
std::cout << (a < c ? "true" : "false") << "\n"; //true
}
С++ 20 должен принести нам сравнения по умолчанию (https://en.cppreference.com/w/cpp/language/default_comparisons). Поэтому, если класс определяет operator<=>
как дефолтный, компилятор автоматически сгенерирует operator<=>
==
!=
, <
, <=
, >
И >=
и код для них:
struct Point {
int x;
int y;
int z;
auto operator<=>(const Point&) const = default;
};
Ответ 3
В С++ 14 и выше вы можете использовать эту библиотеку: https://github.com/apolukhin/magic_get/ для извлечения типов членов POD. Затем вы можете написать универсальный оператор сравнения, который не требует памяти настроек исходного объекта для стирания отступов, например:
#include "boost/pfr/precise.hpp"
template<typename T>
void foo(const T& a, const T& b)
{
return boost::pfr::flat_less<T>{}(a, b);
}
Преимущество этого метода состоит в том, что он не модифицирует код, который создает объекты (что может оказаться полезным, когда он не находится под вашим контролем), но он также генерирует дополнительный двоичный код, и компиляция с использованием библиотеки PFR будет выполняться медленнее.
Тем не менее - он наиболее гибкий и чистый, поскольку простой memcmp не дает реальной семантической силы (например, когда вы используете пользовательские операторы сравнения для подтипов ваших POD).
PS: используя библиотеку PFR, вы можете делать несколько других вещей с POD, например, распечатывать их, перебирать элементы и т.д. Проверьте больше примеров здесь:
http://apolukhin.github.io/magic_get/boost_precise_and_flat_reflectio/short_examples_for_the_impatient.html