C++安全有效地将std::weak_ordering转换为int

egdjgwm8  于 2023-06-07  发布在  其他
关注(0)|答案(3)|浏览(136)

C++20引入了一个新的比较类型:std::weak_ordering
它允许表示小于、等于或大于。
然而,一些旧的函数使用int来实现类似的目的。例如qsort,它使用签名

int compar (const void* p1, const void* p2);

如何将std::weak_ordering转换为int,以便在qsort这样的函数中使用?
以下是示例情况:

#include <compare>
#include <iostream>

int main() {
    long a = 2354, b = 1234;
    std::weak_ordering cmp = a <=> b;
    
    if (cmp > 0)  std::cout << "a is greater than b" << std::endl;
    if (cmp == 0) std::cout << "a is equal to b" << std::endl;
    if (cmp < 0)  std::cout << "a is less than b" << std::endl;

    int equivalent_cmp = cmp; // errors
}

在测试中,我注意到使用reinterpret_castint8_t类型确实可以工作,但我不确定这是否是可移植的。

int equivalent_cmp = *(int8_t *)&cmp;

或者等同地,

int equivalent_cmp = *reinterpret_cast<int8_t*>(&cmp);

这样安全吗
此外,还有一些其他的解决方案可以工作,但与这种“不安全”的方法相比效率低下。所有这些都将比上述解决方案慢

int equivalent_cmp = (a > b) - (a < b);

int equivalent_cmp;
    if (cmp < 0)       equivalent_cmp = -1;
    else if (cmp == 0) equivalent_cmp =  0;
    else               equivalent_cmp =  1;

有没有一个更好的解决方案,可以保证工作?

b4wnujal

b4wnujal1#

有没有一个更好的解决方案,可以保证工作?
没有
该标准没有指定排序类的内容或表示。巴里的答案是基于合理的假设,这些假设可能成立,但不能保证。
如果您需要它,最好的办法是编写类似于上一个代码片段的内容

constexpr int ordering_as_int(std::weak_ordering cmp) noexcept {
    return (cmp < 0) ? -1 : ((cmp == 0) ? 0 : 1);
}
xa9qqrwz

xa9qqrwz2#

如何将std::weak_ordering转换为int,以便在qsort这样的函数中使用?
简单的答案是:不要使用qsort,使用std::sort,它的性能会更好。
也就是说,我们知道std::weak_ordering必须有一些整数类型成员,C++20确实有一个机制来提取它:std::bit_cast

static_assert(std::bit_cast<int8_t>(0 <=> 1) == -1);

规则是,要转换的类型(在本例中为int8_t)必须与要转换的类型(在本例中为std::strong_ordering)大小相同。这是对bit_cast的一个约束,所以它是安全的--如果实现实际存储的是int而不是int8_t,那么它将无法编译。
因此,更一般地说,您必须编写一个简短的元程序来确定要转换为的正确的有符号整数类型。
请注意,虽然weak_orderingstrong_ordering将仅实现为存储整数(尽管不是标准中所示的int),但partial_ordering可能 * 不会 * 实现为存储intbool-它可能仍将实现为单个整数。所以这一招行不通。

z4iuyo4d

z4iuyo4d3#

我最近需要这个转换,并提出了这个解决方案,如果可能的话,它将使用bit_cast,否则将回落到较慢的方法。

template<typename T>
concept Sane_ordering =
    std::bit_cast<signed char>(T::equivalent) == 0 &&
    std::bit_cast<signed char>(T::less) < 0 &&
    std::bit_cast<signed char>(T::greater) > 0;

template<Sane_ordering T>
inline constexpr int to_int_helper(T ordering) {
    return std::bit_cast<signed char>(ordering);
}

template<typename T>
inline constexpr int to_int_helper(T ordering) {
    return (ordering > 0) - (ordering < 0);
}

inline constexpr int to_int(std::weak_ordering ordering) {
    return to_int_helper(ordering);
}

相关问题