C++20 中是否有浮点数的包装器可以让我默认使用飞船运算符?
Is there a wrapper for floating point numbers in C++20 that would enable me to default the spaceship operator?
我在看 "Using C++20 three way comparison - Jonathan Müller - Meeting C++ 2019" 谈话,它提到 类 包含浮点成员的问题。
问题来自这样一个事实,即涉及 NaN(s) 的 IEEE 754 比较很奇怪并且不提供总排序。
Talk 提供了一种解决此问题的方法,例如使用 strong_order 或在实现 <=> 时手动忽略 NaN 值(假设值永远不是 NaN)。
我的问题是是否有一些库包装器可以让我说 "I promise" 我的浮点数永远不是 NaN 或 可以进行缓慢但有效的比较在花车上(更慢但更安全,因为现在订购了 NaN)。我的目标是通过使成员 float spaceship 友好(因此我可以默认 spaceship)来避免手动实现 spaceship。
使用演讲中的例子:
// original class
struct Temperature{
double value;
};
struct TemperatureNoNan{
std::a_number<double> value; // I promise value will never be NaN
// Now spaceship defaulting works
};
struct TemperatureStrongO{
std::s_ordered<double> value; // I want strong ordering(2 diff NaNs are not the same)
// Now spaceship defaulting works
};
标准库中没有这方面的内容,但实现起来很简单:
template<class T,class O=std::weak_ordering>
struct a_number {
T t;
O operator<=>(const a_number &rhs) const {
return t<rhs.t ? O::less : t==rhs.t ? O::equivalent : O::greater;
}
};
template<class T>
struct s_ordered {
T t;
auto operator<=>(const s_number &rhs) const {
return std::strong_order(t,rhs.t);
}
};
…使用任何其他转换运算符或所需的其他便利。
"I promise" that my floats are never NaN
template <std::floating_point T>
struct NeverNaN {
T val;
constexpr NeverNaN(T val) : val(val) { }
constexpr operator T() const { return val; }
constexpr bool operator==(NeverNaN const&) const = default;
constexpr std::strong_ordering operator<=>(NeverNaN const& rhs) const {
auto c = val <=> rhs.val;
assert(c != std::partial_ordering::unordered);
return c > 0 ? std::strong_ordering::greater :
c < 0 ? std::strong_ordering::less :
std::strong_ordering::equal;
}
};
不幸的是,没有什么好的方法可以 "lift" 这样的比较类别。而且目前优化得不是很好。
that would do slow but valid comparisons on floats(slower but safer since NaNs are now ordered)
这个有特定的库支持,通过 std::strong_order()
或 std::weak_order()
[cmp.alg] 取决于你想要什么样的比较:
template <std::floating_point T>
struct TotallyOrdered {
T val;
constexpr TotallyOrdered(T val) : val(val) { }
constexpr operator T() const { return val; }
// depends on whether or not you want == NaN to still be false?
// might need to be: return (*this <=> rhs) == 0;
constexpr bool operator==(TotallyOrdered const&) const = default;
constexpr auto operator<=>(TotallyOrdered const& rhs) const {
return std::strong_order(val, rhs.val);
// ... or std::weak_order(val, rhs.val)
}
};
我在看 "Using C++20 three way comparison - Jonathan Müller - Meeting C++ 2019" 谈话,它提到 类 包含浮点成员的问题。
问题来自这样一个事实,即涉及 NaN(s) 的 IEEE 754 比较很奇怪并且不提供总排序。 Talk 提供了一种解决此问题的方法,例如使用 strong_order 或在实现 <=> 时手动忽略 NaN 值(假设值永远不是 NaN)。
我的问题是是否有一些库包装器可以让我说 "I promise" 我的浮点数永远不是 NaN 或 可以进行缓慢但有效的比较在花车上(更慢但更安全,因为现在订购了 NaN)。我的目标是通过使成员 float spaceship 友好(因此我可以默认 spaceship)来避免手动实现 spaceship。
使用演讲中的例子:
// original class
struct Temperature{
double value;
};
struct TemperatureNoNan{
std::a_number<double> value; // I promise value will never be NaN
// Now spaceship defaulting works
};
struct TemperatureStrongO{
std::s_ordered<double> value; // I want strong ordering(2 diff NaNs are not the same)
// Now spaceship defaulting works
};
标准库中没有这方面的内容,但实现起来很简单:
template<class T,class O=std::weak_ordering>
struct a_number {
T t;
O operator<=>(const a_number &rhs) const {
return t<rhs.t ? O::less : t==rhs.t ? O::equivalent : O::greater;
}
};
template<class T>
struct s_ordered {
T t;
auto operator<=>(const s_number &rhs) const {
return std::strong_order(t,rhs.t);
}
};
…使用任何其他转换运算符或所需的其他便利。
"I promise" that my floats are never NaN
template <std::floating_point T>
struct NeverNaN {
T val;
constexpr NeverNaN(T val) : val(val) { }
constexpr operator T() const { return val; }
constexpr bool operator==(NeverNaN const&) const = default;
constexpr std::strong_ordering operator<=>(NeverNaN const& rhs) const {
auto c = val <=> rhs.val;
assert(c != std::partial_ordering::unordered);
return c > 0 ? std::strong_ordering::greater :
c < 0 ? std::strong_ordering::less :
std::strong_ordering::equal;
}
};
不幸的是,没有什么好的方法可以 "lift" 这样的比较类别。而且目前优化得不是很好。
that would do slow but valid comparisons on floats(slower but safer since NaNs are now ordered)
这个有特定的库支持,通过 std::strong_order()
或 std::weak_order()
[cmp.alg] 取决于你想要什么样的比较:
template <std::floating_point T>
struct TotallyOrdered {
T val;
constexpr TotallyOrdered(T val) : val(val) { }
constexpr operator T() const { return val; }
// depends on whether or not you want == NaN to still be false?
// might need to be: return (*this <=> rhs) == 0;
constexpr bool operator==(TotallyOrdered const&) const = default;
constexpr auto operator<=>(TotallyOrdered const& rhs) const {
return std::strong_order(val, rhs.val);
// ... or std::weak_order(val, rhs.val)
}
};