避免代码重复的最佳方法,定义比较运算符"<,<=,>,>=,==,!=",但要考虑NaN?
Best way to avoid code duplication defining comparison operators `<, <=, >, >=, ==, !=`, but taking into account NaNs?
I数学,x <= y
等价于!(x > y)
。这对于浮点运算是正确的,在大多数情况下,但并非总是如此。当x
或y
为NaN时,x <= y
不等于!(x > y)
,因为比较NaN
和任何东西总是返回false
。但是,x <= y <=> !(x > y)
在大多数情况下是正确的。
现在,假设我正在编写一个包含浮点值的类,并且希望为该类定义比较操作符。为了明确起见,假设我正在编写一个高精度浮点数,它在内部使用一个或多个double
值来存储高精度数。从数学上讲,该类的x < y
定义已经定义了所有其他操作符(如果我与比较操作符的通常语义一致的话)。但是NaN
打破了这种数学上的精确。也许我不得不把这些算子分开写,只是为了考虑到nan。但是有没有更好的办法呢?我的问题是:如何尽可能避免代码重复,同时仍然尊重NaN
的行为?
相关:http://www.boost.org/doc/libs/1_59_0/libs/utility/operators.htm。boost/operators如何解决这个问题?
注意:我标记这个问题c++
,因为这是我所理解的。请用那种语言写例子
我个人会使用与这个答案类似的技术,该技术基于operator<()
定义比较函数,产生严格的弱顺序。对于具有空值的类型,其比较总是产生false
,操作将根据operator<()
定义,为所有非空值提供严格的弱顺序和is_null()
测试。
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);
}
}
可以这样使用:
#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(); }
相同主题的变体可以是一个比较函数的实现,该比较函数由比较函数参数化,并负责适当地为比较函数提供参数。例如:
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);
}
};
我可以想象有多个这样的操作生成名称空间来支持对常见情况的合适选择。另一个选项可能是与浮点数不同的排序,例如,将空值视为最小值或最大值。由于有些人使用NaN装箱,因此提供不同NaN值的顺序并将NaN值安排在合适的位置可能是合理的。例如,使用底层的位表示提供浮点值的总顺序,这可能适用于将对象用作有序容器中的键,尽管该顺序可能与operator<()
创建的顺序不同。
定义运算符<你需要处理NaN的案子。出于排序和比较的目的,如果您将NaN视为小于任何非NaN,则可以这样做:>
bool operator<(double l, double r) {
if (isnan(l)) {
if (isnan(r)) return false; // NaN == NaN
return true; // NaN < rational
}
return l < r; // if r is NaN will return false, which is how we've defined it
}
其他操作符用operator<并且不需要手动编写代码。>
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- EASTL矢量<向量<int>>连续的
- C - 创建矢量&lt; vector&lt; double&gt;&gt;矩阵具有分配而不是inizializ
- 为什么将此对向量&lt; map&lt; int,int&gt;&gt;中的地图进行更新.失败
- C :对矢量进行排序&lt; struct&gt;(结构有2个整数)基于结构的整数之一
- 明确的专业化“ CheckIntmap&lt;&gt;”实例化
- 什么是模板&lt;&gt;inline bla bla
- 编辑C Qlist&lt; object*&gt; gt;QML代码和一些QML警告中的模型
- eigen :: llt&lt;eigen :: matrixxd&gt;具有不完整的类型
- 错误,包括&lt; ctype&gt;在原子上使用C 11
- std::vector<;uint8_t>;当C++11/14启用时,手动复制而不是调用memcpy
- 如何加入向量&lt; int&gt;到C 中的单个INT
- 是std :: set&lt; std :: future&gt;不可能存在
- 是numeric_limits&lt; int&gt; :: is_modulo从逻辑上矛盾
- opencv 2.4.7在iOS错误背景_segm.hpp #include&lt; list&gt;未找到
- 在修改列表后,std :: list&lt; t&gt; :: end()的值是否会更改
- ///<评论></评论>在Visual Studio中