Как сравнивать объекты типов POD - PullRequest
14 голосов
/ 30 июня 2011

Этот пример:

#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, и я не могу изменить способ сравнения.

Предполагая, что bool занимает 1 байт (это верно для моей системы), если я изменю struct A на это:

struct A
{
  int a;
  bool b;
  char dummy[3];
};

тогда он отлично работает в моей системе (вывод true).

Есть ли что-нибудь еще, что я мог бы сделать, чтобы решить вышеуказанную проблему (получить вывод true)?

Ответы [ 3 ]

17 голосов
/ 30 июня 2011

Первый не работает из-за заполнения в структуре.Заполнение имеет разные битовые комбинации для обоих объектов.

Если вы используете 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;

OnlineДемоверсии:


Кстати, вы можете написать operator<, operator== и т. Д., Также для POD.

3 голосов
/ 04 февраля 2019

Начиная с C ++ 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

}

C ++ 20 должен принести нам сравнения по умолчанию (https://en.cppreference.com/w/cpp/language/default_comparisons). Так, если класс определяет operator<=> как значение по умолчанию, компилятор автоматически сгенерирует ==, !=, <, <=, > и >= операторы и код для них:

struct Point {
    int x;
    int y;
    int z;    

    auto operator<=>(const Point&) const = default;
};
1 голос
/ 06 февраля 2019

В C ++ 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

...