5

我正在观看“使用 C++20 三向比较 - Jonathan Müller - Meeting C++ 2019”的演讲,其中提到了包含浮点成员的类的问题。

问题来自这样一个事实,即涉及 NaN(s) 的 IEEE 754 比较很奇怪并且不提供总排序。Talk 提供了一种解决此问题的方法,例如使用strong_order或在实现 <=> 时手动忽略 NaN 值(假设值永远不是 NaN)。

我的问题是,是否有一些库包装器可以让我说“我保证”我的浮点数永远不是 NaN ,或者会在浮点数上进行缓慢但有效的比较(因为现在订购了 NaN,所以速度较慢但更安全)。我的目标是通过使成员浮动飞船友好来避免手动实现飞船(所以我可以默认飞船)。

使用演讲中的示例:

// 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
};
4

2 回答 2

3

“我保证”我的花车永远不是 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;
    }
};

不幸的是,没有像这样“提升”比较类别的好方法。而且目前优化得不是很好。

这将对浮点数进行缓慢但有效的比较(因为现在订购了 NaN,所以速度较慢但更安全)

这个有特定的库支持,通过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)
    }
};
于 2019-12-22T16:09:25.610 回答
2

标准库中对此没有任何内容,但实现起来很简单:

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

…与任何其他转换运算符或其他所需的便利。

于 2019-12-22T16:08:57.650 回答