简体   繁体   English

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

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

EDIT: This has nothing to do with spaceship.编辑:这与宇宙飞船无关。 It is just that the use of spaceship obfuscated the real issue in my code (see answer for details).只是使用 spaceship 混淆了我的代码中的真正问题(有关详细信息,请参阅答案)。

I was surprised by the output of this program : (If you like puzzles feel free to open the Godbolt link and try to spot the cause yourself)我对这个程序的 output 感到惊讶:(如果您喜欢谜题,请随时打开 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;
}

output: output:

false错误的
false错误的
false错误的
false错误的

After a bit of blaming Godbolt caching... I have figured out that the problem is that I was comparing TotallyOrdered<float> and TotallyOrdered<double> (adding f after 1234.567 gives expected output).在指责 Godbolt 缓存之后......我发现问题在于我正在比较TotallyOrdered<float>TotallyOrdered<double> (在1234.567之后添加f会产生预期的输出)。 My questions are:我的问题是:

  • Why is this allowed?为什么允许这样做? (Not asking if this is standard behavior; it is, but curious about design intention.) (不问这是否是标准行为;它是,但对设计意图感到好奇。)
  • Why do comparisons give none of the "enums" in strong_ordering?为什么比较没有给出strong_ordering中的“枚举”? It looks like I get a partial order when I do a mixed comparison although I have defined only strong_order <=> .尽管我只定义了strong_order <=> ,但当我进行混合比较时,我似乎得到了部分顺序。
  • How can I force that only "exact +-cvref" comparisons (that give a std::strong_ordering result) compile, preventing comparisons that give std::partial_ordering ?如何强制仅“精确 +-cvref”比较(给出std::strong_ordering结果)编译,防止给出std::partial_ordering的比较?

It's allowed because your conversion operator to T is not explicit.这是允许的,因为您到T的转换运算符不明确。 This allows both sides of the comparison to undergo a user-defined conversion to their respective T .这允许比较的双方进行用户定义的转换为各自的T So you end up with a float and a double .所以你最终得到一个float和一个double And then those can both be converted to double and a comparison can happen.然后它们都可以转换为double并且可以进行比较。 But that comparison returns an std::partial_ordering , not an std::strong_ordering .但该比较返回std::partial_ordering ,而不是std::strong_ordering

Note that std::strong_ordering can be compared to a bool, which is why your code compiles in the first place.请注意, std::strong_ordering可以与 bool 进行比较,这就是您的代码首先编译的原因。 Although cppreference.com does note that:虽然 cppreference.com 确实注意到:

The behavior of a program that attempts to compare a strong_ordering with anything other than the integer literal 0 is undefined.尝试将 strong_ordering 与 integer 文字 0 以外的任何内容进行比较的程序的行为未定义。

I'm not a 100% sure whether your program is displaying undefined behavior, or whether there's some more conversion/promotion "magic" going on.我不是 100% 确定您的程序是否正在显示未定义的行为,或者是否有更多的转换/促销“魔术”正在进行。

Either way, if you change your conversion operator to be explicit, the code won't compile anymore.无论哪种方式,如果您将转换运算符更改为显式,代码将不再编译。 Which I guess is what you actually want?我猜这是你真正想要的?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C ++解析器如何区分比较和模板实例化? - How does a parser for C++ differentiate between comparisons and template instantiations? 在可以区分比较和模板实例之前,C ++的解析器会做什么? - What does a parser for C++ do until it can differentiate between comparisons and template instantiations? 为什么为模板实例声明运行时多态性会导致链接器错误? - Why does declaring runtime polymorphism for template instantiations result in linker error? 具有不同实例化的静态模板类变量是否相同? - Are static template class variables with different instantiations the same? 为什么模板实例化在这里永远存在? - Why template instantiations go on forever here? 为什么全球飞船运营商的行为不如预期? - Why does global spaceship operator not behave as expected? 如何在同一个可变参数模板的不同实例之间进行转换? - How to convert between different instantiations of the same variadic template? 为什么只有一些C ++模板实例在共享库中导出? - Why are only some of these C++ template instantiations exported in a shared library? 链接器如何处理跨翻译单元的相同模板实例化? - How does the linker handle identical template instantiations across translation units? “命名”模板类实例化 - “Naming” template class instantiations
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM