递归地删除缺少模板特化的值类型

时间:2016-04-22 12:02:25

标签: c++ templates template-specialization

我现在有一个模板,它从作为参数给出的存储位数推导出具体类型:

template<unsigned char BITS>
struct Register {
    using type = unsigned long;
};

template<>
struct Register<0> {
    using type = void;
};

template<>
struct Register<8> {
    using type = unsigned char;
};

template<>
struct Register<16> {
    using type = unsigned short;
};

template<>
struct Register<32> {
    using type = unsigned long;
};

是否有可能以这样的方式扩展此模板:对于给定的位数,它会自动推断出下一个更高的特化类型? 这意味着:

  • 注册&LT; 1..8&GT;结果在专业化注册&lt; 8&gt;
  • 注册&LT; 9..16&GT;实际上使用Register&lt; 16&gt;和
  • 注册&LT; 17..32&GT;导致注册&lt; 32&gt;。

我认为像Register这样的解决方案正在推导出类型Register。但我不确定这是否是正确的做法。

但是对于所有值&gt; = 33,它应该推断出类型为void或Register&lt; 0&gt ;.

示例:

Register<4>::type value;  // Register<8> will be used
Register<33>::type value = 1; // Compile error -> type is void

这可行吗?

1 个答案:

答案 0 :(得分:3)

首先,您需要引入一个定义递归的基本模板:

template< int N > struct impl_Int
{
    using type = impl_Int<N+1>::type;
};

接下来,定义固定尺寸的专业化:

const int impl_CHAR = sizeof(signed char) != sizeof(short)
    ? sizeof(signed char) * CHAR_BIT
    : INT_MIN;
template<> struct Register<impl_CHAR>
{
    using type = char;
};

const int impl_SHORT = sizeof(short) != sizeof(int)
    ? sizeof(short) * CHAR_BIT
    : INT_MIN + 1;
template<> struct Register<impl_SHORT>
{
    using type = short;
};

const int impl_INT = sizeof(int) != sizeof(long)
    ? sizeof(int) * CHAR_BIT
    : INT_MIN + 2;
template<> struct Register<impl_INT>
{
    using type = int;
};

const int impl_LONG = sizeof(long) * CHAR_BIT;
template<> struct Register<impl_LONG>
{
   using type = long;
};

最后,定义终止专业化:

template<> struct Register<INT_MAX>
{
    using type = void;
};

有关实例,请参阅here

对于生产实现,您可能会在impl命名空间中隐藏这些常量值,然后写:

template <int N>
using Register = impl::Register<N>;

另请注意,您可以通过使用C ++ 11 fixed width integers并对其大小进行硬编码来简化此操作。这将消除对常量的需求:

#include <climits>
#include <cstdint>

template< int N > struct Register
{
    using type = typename Register<N+1>::type;
};

template<> struct Register<8>
{
    using type = std::int8_t;
};

template<> struct Register<16>
{
    using type = std::int16_t;
};

template<> struct Register<32>
{
    using type = std::int32_t;
};

template<> struct Register<64>
{
    using type = void;
};

int main() {
    using T1 = Register<3>::type;
    using T2 = Register<35>::type;
    T1 v1;
    T2 v2; // This line fails to compile: T2 is void.
}

Live example