为什么 spaceship 允许混合比较(不同的模板实例化)和无意义的结果?

Why does spaceship allow mixed comparisons (different template instantiations) with nonsense results?

编辑:这与宇宙飞船无关。只是 spaceship 的使用混淆了我代码中的真正问题(详见答案)。

我对这个 program 的输出感到惊讶: (如果你喜欢拼图,请随时打开 Godbolt link 并尝试自己找出原因)

#include <cstdint>
#include <cassert>
#include <compare>
#include <cmath>
#include <iostream>
#include <limits>

template<typename T>
struct TotallyOrdered
{
    T val;
    constexpr TotallyOrdered(T val) :
        val(val) {}
    constexpr operator T() const { return val; }
    constexpr std::strong_ordering operator<=>(TotallyOrdered const& other) const
    {
        if (std::isnan(val) && std::isnan(other.val))
        {
            return std::strong_ordering::equal;
        }
        if (std::isnan(val))
        {
            return std::strong_ordering::less;
        }
        if (std::isnan(other.val))
        {
            return std::strong_ordering::greater;
        }
        if (val < other.val)
        {
            return std::strong_ordering::less;
        }
        else if (val == other.val)
        {
            return std::strong_ordering::equal;
        }
        else
        {
            assert(val > other.val);
            return std::strong_ordering::greater;
        }
    }
};



int main()
{
    const auto qNan = std::numeric_limits<float>::quiet_NaN();
    std::cout << std::boolalpha;
    std::cout << ((TotallyOrdered{qNan} <=> TotallyOrdered{1234.567}) ==  std::strong_ordering::less) << std::endl;
    std::cout << ((TotallyOrdered{qNan} <=> TotallyOrdered{1234.567}) ==  std::strong_ordering::equal) << std::endl;
    std::cout << ((TotallyOrdered{qNan} <=> TotallyOrdered{1234.567}) ==  std::strong_ordering::equivalent) << std::endl;
    std::cout << ((TotallyOrdered{qNan} <=> TotallyOrdered{1234.567}) ==  std::strong_ordering::greater) << std::endl;
}

输出:

false
false
false
false

经过一番责备 Godbolt 缓存之后...我发现问题是我在比较 TotallyOrdered<float>TotallyOrdered<double>(在 [=14= 之后添加 f ] 给出预期的输出)。 我的问题是:

这是允许的,因为您到 T 的转换运算符不明确。这允许比较的双方进行用户定义的转换到各自的 T。所以你最终得到一个 float 和一个 double。然后它们都可以转换为 double 并且可以进行比较。但是那个比较 returns 一个 std::partial_ordering,而不是 std::strong_ordering.

请注意,std::strong_ordering 可以与 bool 进行比较,这就是您的代码首先编译的原因。虽然 cppreference.com 确实注意到:

The behavior of a program that attempts to compare a strong_ordering with anything other than the integer literal ​0​ is undefined.

我不能 100% 确定您的程序是否正在显示未定义的行为,或者是否有更多 conversion/promotion“魔法”在发生。

无论哪种方式,如果您将转换运算符更改为显式,代码将无法再编译。我猜你真正想要的是什么?