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
精彩评论