如何专门针对多个整数值范围的 C++ 模板?

How can I specialize a C++ template for multiple ranges of integer values?

我正在尝试创建一个模板 class 来处理比特流。我想在模板中声明一个基础整数类型,该类型将根据模板参数(一个int,位数)。我找到了关于这个主题的两个答案 (How can I specialize a C++ template for a range of integer values? and Integer range based template specialisation) 并实现了以下代码:

template<int BITS>
class MyClass {
   typedef typename
      std::conditional< BITS <= 8,  uint8_t,
      std::conditional< BITS <= 16, uint16_t,
      std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
         int_type;
    ...
}

在我的程序中,我实例化了 MyClass<32>,但是在编译时,我收到以下错误:

no known conversion for argument 1 from ‘uint32_t {aka unsigned int}’ to ‘MyClass<32>::int_type {aka std::conditional<false, short unsigned int, std::conditional<true, unsigned int, long unsigned int> >}’

如果我实例化 MyClass<8>,一切正常。所以看起来只有std::conditional的第一层实际上被展开了。

知道如何正确执行此操作吗?

编辑: 我之前没有说明这一点,但我正在寻找一种解决方案,它也适用于任何位大小的实例化(只要它是 64 位的)最多)。所以我希望 MyClass<27> 也能正常工作(选择 uint32_t)。

越简单越好:

template<unsigned nbits> struct uint {};

template<> struct uint<8> { using type = uint8_t; };
template<> struct uint<16> { using type = uint16_t; };
template<> struct uint<32> { using type = uint32_t; };
template<> struct uint<64> { using type = uint64_t; };

template<int nbits>
struct MyClass { using int_type = typename uint<(nbits/8)*8>::type; };

回答您的编辑并使您的原始代码正常工作。

template<int BITS>
class MyClass {
   using int_type =
      typename std::conditional< BITS <= 8,  uint8_t,
      typename std::conditional< BITS <= 16, uint16_t,
      typename std::conditional< BITS <= 32, uint32_t, uint64_t >::type >::type >::type;
   public:
   int_type i;
};

问题的解决方案已经在中提供了。

理解错误信息对我很有启发。

您已将 int_type 定义为:

typedef typename
  std::conditional< BITS <= 8,  uint8_t,
  std::conditional< BITS <= 16, uint16_t,
  std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
     int_type;

它在 BITS <= 8 上正常工作,因为

  std::conditional< BITS <= 16, uint16_t,
  std::conditional< BITS <= 32, uint32_t, uint64_t > >

部分基本上被忽略了。

假设您使用 MyClass<16>。然后, uint8_t 被忽略。 int_type 是:

  std::conditional< BITS <= 16, uint16_t,
  std::conditional< BITS <= 32, uint32_t, uint64_t > >

可以简化为:

  std::conditional< true, uint16_t,
  std::conditional< true, uint32_t, uint64_t > >

不幸的是,这是你不使用 std::conditional<...>::type 得到的类型。

假设您使用 MyClass<32>。那么 int_type 是:

  std::conditional< BITS <= 16, uint16_t,
  std::conditional< BITS <= 32, uint32_t, uint64_t > >

可以简化为:

  std::conditional< false, uint16_t,
  std::conditional< true, uint32_t, uint64_t > >

这就是你得到的类型。

您可以通过打印相应 type_info 对象的名称来了解这些类型。

示例程序:

#include <iostream>
#include <typeinfo>
#include <type_traits>
#include <cstdint>

template<int BITS>
struct MyClass
{
   typedef typename
      std::conditional< BITS <= 8,  uint8_t,
      std::conditional< BITS <= 16, uint16_t,
      std::conditional< BITS <= 32, uint32_t, uint64_t > > >::type
         int_type;
};

int main()
{
   typename MyClass<8>::int_type a;
   std::cout << typeid(a).name() << std::endl;

   typename MyClass<16>::int_type b;
   std::cout << typeid(b).name() << std::endl;

   typename MyClass<32>::int_type c;
   std::cout << typeid(c).name() << std::endl;

   typename MyClass<60>::int_type d;
   std::cout << typeid(d).name() << std::endl;
}

输出,使用 g++ 5.4.0:

h
St11conditionalILb1EtS_ILb1EjmEE
St11conditionalILb0EtS_ILb1EjmEE
St11conditionalILb0EtS_ILb0EjmEE

您应该看看 Boost.Integer:它的 Integer Type Selection 完全符合您的要求。在您的情况下,您应该使用 boost::uint_t<N>::least:

#include <boost/integer.hpp>

template<int BITS>
class MyClass {
   using int_type = typename boost::uint_t<BITS>::least;
};