为什么 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
] 给出预期的输出)。
我的问题是:
- 为什么允许这样做? (不是问这是否是标准行为;是,但对设计意图感到好奇。)
- 为什么比较会给出 strong_ordering 中的 none 个“枚举”?虽然我只定义了
strong_order
<=>
. ,但看起来我在进行混合比较时得到了部分顺序
- 我怎样才能强制只编译“精确的 +-cvref”比较(给出
std::strong_ordering
结果),防止给出 std::partial_ordering
的比较?
这是允许的,因为您到 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“魔法”在发生。
无论哪种方式,如果您将转换运算符更改为显式,代码将无法再编译。我猜你真正想要的是什么?
编辑:这与宇宙飞船无关。只是 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
] 给出预期的输出)。
我的问题是:
- 为什么允许这样做? (不是问这是否是标准行为;是,但对设计意图感到好奇。)
- 为什么比较会给出 strong_ordering 中的 none 个“枚举”?虽然我只定义了
strong_order
<=>
. ,但看起来我在进行混合比较时得到了部分顺序
- 我怎样才能强制只编译“精确的 +-cvref”比较(给出
std::strong_ordering
结果),防止给出std::partial_ordering
的比较?
这是允许的,因为您到 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“魔法”在发生。
无论哪种方式,如果您将转换运算符更改为显式,代码将无法再编译。我猜你真正想要的是什么?