c++ - Best way to avoid code duplication defining comparison operators `<, <=, >, >=, ==, !=`, but taking into account NaNs? -
i mathematics, x <= y
equivalent !(x > y)
. true floating-point arithmetic, in cases, not always. when x
or y
nan, x <= y
not equivalent !(x > y)
, because comparing nan
returns false
. still, x <= y <=> !(x > y)
true of time.
now, suppose writing class contains floating-point values, , want define comparison operators class. definiteness, suppose writing high-precision floating-point number, uses 1 or more double
values internally store high-precision number. mathematically, definition of x < y
class defines other operators (if being consistent usual semantics of comparison operators). nan
s break mathematical nicety. maybe forced write many of these operators separately, take account nans. there better way? question is: how can avoid code duplication as possible , still respect behavior of nan
?
related: http://www.boost.org/doc/libs/1_59_0/libs/utility/operators.htm. how boost/operators resolve issue?
note: tagged question c++
because that's understand. please write examples in language.
personally use similar technique in this answer defines comparison function based on operator<()
yielding strict weak order. types null value meant have comparisons yield false
operations defined in terms of operator<()
providing strict weak order on non-null value , is_null()
test.
for example, code this:
namespace nullable_relational { struct tag {}; template <typename t> bool non_null(t const& lhs, t const& rhs) { return !is_null(lhs) && !is_null(rhs); } template <typename t> bool operator== (t const& lhs, t const& rhs) { return non_null(lhs, rhs) && !(rhs < lhs) && !(lhs < rhs); } template <typename t> bool operator!= (t const& lhs, t const& rhs) { return non_null(lhs, rhs) || !(lhs == rhs); } template <typename t> bool operator> (t const& lhs, t const& rhs) { return non_null(lhs, rhs) && rhs < lhs; } template <typename t> bool operator<= (t const& lhs, t const& rhs) { return non_null(lhs, rhs) && !(rhs < lhs); } template <typename t> bool operator>= (t const& lhs, t const& rhs) { return non_null(lhs, rhs) && !(lhs < rhs); } }
it use this:
#include <cmath> class foo : private nullable_relational::tag { double value; public: foo(double value): value(value) {} bool is_null() const { return std::isnan(this->value); } bool operator< (foo const& other) const { return this->value < other.value; } }; bool is_null(foo const& value) { return value.is_null(); }
a variation of same theme implementation in terms of 1 compare function parameterized comparison function , responsible appropriately feed comparison function parameters. example:
namespace compare_relational { struct tag {}; template <typename t> bool operator== (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs == rhs; }); } template <typename t> bool operator!= (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs != rhs; }); } template <typename t> bool operator< (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs < rhs; }); } template <typename t> bool operator> (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs > rhs; }); } template <typename t> bool operator<= (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs <= rhs; }); } template <typename t> bool operator>= (t const& lhs, t const& rhs) { return compare(lhs, rhs, [](auto&& lhs, auto&& rhs){ return lhs >= rhs; }); } } class foo : private compare_relational::tag { double value; public: foo(double value): value(value) {} template <typename compare> friend bool compare(foo const& f0, foo const& f1, compare&& predicate) { return predicate(f0.value, f1.value); } };
i imagine having multiple of these operation-generating namespaces support suitable choice common situations. option different ordering floating points and, e.g., consider null value smallest or largest value. since people use nan-boxing may reasonable provide order on different nan values , arrange nan values @ suitable places. example, using underlying bit representation provide total order of floating point values may suitable using objects key in ordered container although order might different order created operator<()
.
Comments
Post a Comment