模板:如何选择包含n位的最小int类型?

时间:2018-12-11 15:06:50

标签: c++ c++11 templates

对于std::bitset(禁止std)的自己实现,我使用了uint_fast32_t,因为它在64位CPU上速度更快。审查意见是为了节省小型设备的空间,例如bitset <6>不应使用8个字节。如果您考虑在结构中对齐,则浪费会更大。

使用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位,并且在某些用例中,例如当大小很重要时。 Size == 4表示将浪费7个字节。

5 个答案:

答案 0 :(得分:6)

鉴于可能选择的类型很少,我只是对条件进行硬编码:

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>>;

答案 1 :(得分:3)

有助手

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...
};

答案 2 :(得分:1)

您可以将递归模板类与非类型模板参数一起用于位限制,并为相应的整数类型使用成员类型别名。

尽管您不需要自己编写这样的模板,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

答案 3 :(得分:0)

冗长的解决方案,具有更少的魔力。参考: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;
};

答案 4 :(得分:0)

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

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{};
}