如何为多个整数值范围专门化C++模板?

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

本文关键字:专门化 范围 C++ 模板 整数      更新时间:2023-10-16

我正在尝试创建一个用于处理位流的模板类。我想在模板中声明一个底层整数类型,该类型将解析为uint8_tuint16_tuint32_tuint64_t,具体取决于模板参数(整数、位数)。我找到了关于这个主题的两个答案(如何为一系列整数值专门化C++模板?和基于整数范围的模板专用化)并实现了以下代码:

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

@super的答案中已经提供了该问题的解决方案。

理解错误消息对我很有帮助。

您已将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:它的整数类型选择正是你正在寻找的。在您的情况下,您应该使用boost::uint_t<N>::least

#include <boost/integer.hpp>
template<int BITS>
class MyClass {
using int_type = typename boost::uint_t<BITS>::least;
};