为什么宇宙飞船允许混合比较(不同的模板实例)与无意义的结果?

NoS*_*tAl 5 c++ spaceship-operator c++20

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

我对这个程序的输出感到惊讶:(如果你喜欢谜题,请随意打开 Godbolt 链接并尝试自己找出原因)

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

假的
假的
假的
假的

在有点指责 Godbolt 缓存之后......我发现问题在于我正在比较TotallyOrdered<float>TotallyOrdered<double>f1234.567给出预期输出之后添加)。我的问题是:

  • 为什么这是允许的?(不是问这是否是标准行为;它是,但对设计意图感到好奇。)
  • 为什么比较在 strong_ordering 中没有给出任何“枚举”?虽然我只定义了strong_order <=>.
  • 我怎样才能强制只有“精确+-cvref”比较(给出std::strong_ordering结果)编译,阻止给出的比较std::partial_ordering

Dar*_*uuk 4

这是允许的,因为您的转换运算符 toT不明确。这允许比较的双方都进行用户定义的转换为其各自的T. 所以你最终得到 afloat和 a double。然后这些都可以转换double并进行比较。但该比较返回的是 an std::partial_ordering,而不是 an std::strong_ordering

\n

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

\n
\n

尝试将 Strong_ordering 与整数文字 \xe2\x80\x8b0\xe2\x80\x8b 以外的任何内容进行比较的程序的行为是未定义的。

\n
\n

我不能 100% 确定您的程序是否显示未定义的行为,或者是否存在更多转换/促销“魔法”。

\n

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

\n

  • @NoSenseEtAl 是的,我猜确切的值是 `std::partial_ordering::unordered`。我指的是答案的这句话:“*但是当然,比较会给你一个布尔值,而不是你期望的枚举。*”。我不太确定“*`std::strong_ordering`是否可以与 bool*”部分进行比较,我在 cppreference 中找不到它。 (3认同)
  • @NoSenseEtAl 在某些 IDE(例如 Visual Studio)上,您可以按住 ctrl 并用鼠标单击运算符,它将带您到实际调用的重载,或者让您知道该运算符没有重载。如果将鼠标悬停,您可以看到类似“operator&lt;=&gt; (double, double)”的声明。因此,这是调试运算符重载问题的好方法。 (2认同)