开发者

How to compare objects of POD types

开发者 https://www.devze.com 2023-03-16 21:34 出处:网络
This example : #include <iostream> #include <cstring> struct A { inta; bool b; }; bool foo( const A a1, const A a2 )

This example :

#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;
}

is going to produce false, because of padding.

开发者_如何学运维I do not have access to the foo function, and I can not change the way the comparison is done.

Assuming a bool occupies 1 byte (that is true on my system), if I change the struct A to this :

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

then it works fine on my system (the output is true).

Is there anything else I could do to fix the above problem (get the true output)?


The first one is not working because of padding in the struct. The padding is having different bit patterns for both objects.

If you use memset to set all the bits in the object before using it, then it will work:

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 demos:

  • http://www.ideone.com/mVmsn (Original code written by you)
  • http://www.ideone.com/Q13QO (My modification)

By the way, you can write operator<, operator== etc, for PODs also.


Since C++11 we can use tuples for simple POD comparison (tuples use lexicographical comparison for >, <, >= and <= operators, more info on that: 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 should bring us default comparisons (https://en.cppreference.com/w/cpp/language/default_comparisons). So if class defines operator<=> as defaulted, compiler will automatically generate ==, !=, <, <=, > and >= operators and code for them:

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

    auto operator<=>(const Point&) const = default;
};


In C++14 and above you can use this library: https://github.com/apolukhin/magic_get/ to extract member types of POD. Than you can write generic comparison operator, which does not require memsetting memory of original object to erase padding like this:

#include "boost/pfr/precise.hpp"
template<typename T>
void foo(const T& a, const T& b)
{
    return boost::pfr::flat_less<T>{}(a, b);
}

This method has the advantage of not modifying code that creates objects (which can be valuable when it's not under your control), but it also generates additional binary code, and compilation with PFR library will be slower.

Still - it's most flexible and clean, since simple memcmp does not give you real semantic power (for example when you use custom comparison operators on subtypes of your PODs).


PS: using PFR library you can do multiple other things with PODs, such as printing them, iterating over members etc. Check more examples here:

http://apolukhin.github.io/magic_get/boost_precise_and_flat_reflectio/short_examples_for_the_impatient.html

0

精彩评论

暂无评论...
验证码 换一张
取 消