c++ 模板:如何选择最小的int类型,其中包含n位?

ddrv8njm  于 2023-05-02  发布在  其他
关注(0)|答案(6)|浏览(136)

对于std::bitset(std禁止)的实现,我使用了uint_fast32_t,因为它在64位CPU上更快。一个评论是为小集合保存空间,例如。例如,bitset不应该使用8个字节<6>。如果考虑到结构中的对齐,则浪费会更多。
用C++11就可以了。我想优雅地选择:

  • 大小〈= 8:uint8_t
  • 大小〈= 16:uint16_t
  • 大小〈= 32:uint32_t
  • 大小〉32:uint_fast32_t

作为我的类中的存储类型:

template<size_t Size>
struct BitSet {
    typedef <expression> StorageType;
    // an array of that storage type...
};

我只能想到一些相当笨拙的辅助模板,但也许在C++11中有一些更优雅的东西。
编辑:澄清:uint_fast8_t对于原始类来说是合适的,编译器可以选择任何快的。想象大小==1000000。但在某些机器上它可能是64位的,当在某些用例中大小很重要时,e。例如,大小==4,这意味着将浪费7个字节。

mwngjboj

mwngjboj1#

考虑到可能的类型的小选择,我只需要硬编码条件:

using StorageType = std::conditional_t<Size <=  8, uint8_t,
                    std::conditional_t<Size <= 16, uint16_t,
                    std::conditional_t<Size <= 32, uint32_t, uint_fast32_t>>>;

或者,使用类似于@Caleth的答案的技巧:

using StorageType = std::tuple_element_t<(Size > 8) + (Size > 16) + (Size > 32),
                        std::tuple<uint8_t, uint16_t, uint32_t, uint_fast32_t>>;
sr4lhrrt

sr4lhrrt2#

带着帮手

namespace detail {

    template <int overload>
    struct StorageType;

    template <>
    struct StorageType<0>{ using type = uint8_t; };

    template <>
    struct StorageType<1>{ using type = uint16_t; };

    template <>
    struct StorageType<2>{ using type = uint32_t; };

    template <>
    struct StorageType<3>{ using type = uint_fast32_t; };
}

然后我们可以对constexpr个布尔值求和

template<size_t Size>
struct BitSet {
    typedef typename detail::StorageType<(Size > 8) + (Size > 16) + (Size > 32)>::type StorageType;
    // an array of that storage type...
};
jobtbby3

jobtbby33#

您可以使用递归模板类,并将非类型模板参数用于位限制,将成员类型别名用于相应的整数类型。
你不需要自己编写这样的模板,Boost已经涵盖了:boost::uint_t<N>::least(或者boost::uint_t<N>::fast,如果你不是在寻找最小但最快的)。
P.S.如果你打算自己实现这个模板,并且你想要最小整数类型(根据标题中的问题),那么你应该使用std::uint_leastN_t而不是uint_fastN_tuintN_t。前者不一定是最小的类型,后者也不保证在所有系统上都存在。
此外,uint_fast32_t不能保证能够表示超过32位的位,因此对于Size > 32来说,这是一个相当糟糕的选择。我会推荐uint_least64_t代替。

k3fezbri

k3fezbri4#

一个冗长的解决方案,少魔术。参考:https://gcc.godbolt.org/z/P2AGZ8

#include<cstdint>

namespace helper { namespace internal {

enum class Size {
    Uint8,
    Uint16,
    Uint32,
    UintFast32,
};

constexpr Size get_size(int s) {
    return (s <= 8 ) ? Size::Uint8:
           (s <= 16) ? Size::Uint16:
           (s <= 32) ? Size::Uint32: Size::UintFast32;
}

template<Size s>
struct FindTypeH;

template<>
struct FindTypeH<Size::Uint8> {
    using Type = std::uint8_t;
};

template<>
struct FindTypeH<Size::Uint16> {
    using Type = std::uint16_t;
};

template<>
struct FindTypeH<Size::Uint32> {
    using Type = std::uint32_t;
};

template<>
struct FindTypeH<Size::UintFast32> {
    using Type = std::uint_fast32_t;
};
}

template<int S>
struct FindType {
    using Type = typename internal::FindTypeH<internal::get_size(S)>::Type;
};
}

template<int S>
struct Myclass {
    using Type = typename helper::FindType<S>::Type;
};
hc2pp10m

hc2pp10m5#

如果你的编译器支持的话,你也可以使用constexpr if--这可能是最容易阅读的一个。基于以下代码的示例:

template<size_t S>
static constexpr auto _findStorageType() {
    static_assert(S > 0, "Invalid storage size");

    if constexpr (S <= sizeof(uint8_t) * CHAR_BIT) return uint8_t{};
    else if constexpr (S <= sizeof(uint16_t) * CHAR_BIT) return uint16_t{};
    else if constexpr (S <= sizeof(uint32_t) * CHAR_BIT) return uint32_t{};
    else if constexpr (S <= sizeof(uint64_t) * CHAR_BIT) return uint64_t{};
    else return __uint128_t{};
}
ivqmmu1c

ivqmmu1c6#

简单的解决方案:

template<std::size_t MAX_VAL>
using min_unsigned_type = std::tuple_element_t<
    bit_width(MAX_VAL), // See bellow.
    std::tuple<
         std::uint8_t,
         std::uint16_t,
         std::uint32_t,
         std::uint64_t
>>;

这是简单地使用bit_width,即。最小位数,可以适应索引所有类型的std::tuple所需的最大值。
C++20有一个std::bit_width,即constexpr,但如果需要,很容易编写自己的版本。

相关问题