根据模板整数参数选择整数类型

Edg*_*ima 11 c++ templates

我想创建一个类模板,它接受一个无符号整数参数,并且有一个成员,u_其类型是最小的无符号整数类型,它将保存整数参数.

所以:

template <uint64_t k>
class A {
  ??? u_;
};
Run Code Online (Sandbox Code Playgroud)

因为A<0>,u_应该是类型uint8_t.同样的A<255>.因为A<256>,u_应该是类型uint16_t

你会如何实现这个?

Bat*_*eba 10

这段元编程技巧实现了它:

template<unsigned int Y> struct typed
{
    typedef typename typed<(Y & (Y - 1)) == 0 ? Y / 2 : (Y & (Y - 1))>::type type;
};

template<> struct typed<0>
{
    typedef std::uint8_t type;
};

template<> struct typed<256>
{
    typedef std::uint16_t type;
};

template<> struct typed<65536>
{
    typedef std::uint32_t type;
};

/* ToDo - add more specialisations as necessary*/

template<unsigned k> class A
{
public:
    unsigned static const k_ = k; /*constexpr if your compiler supports it*/
    typename typed<k>::type u_;
};
Run Code Online (Sandbox Code Playgroud)

用法完全在问题中.

非专业化模板版本采用先前的类型.typed<0>阻止静态递归.其他专业作为适当类型的锚点.

可评估的编译时(Y & (Y - 1)) == 0 ? Y / 2 : (Y & (Y - 1))通过删除最右边的位来减少实例化的数量,Y直到达到2的幂,然后在其之后除以2.(Acknowledge @ Jarod42).


man*_*lio 8

使用C++ 11,您可以使用std :: conditional:

#include <cassert>
#include <cstdint>
#include <limits>
#include <type_traits>

template<std::uint32_t K>
class A
{
public:
  decltype(K) k_ = K;

  typename std::conditional<K <= UINT8_MAX,
                            std::uint8_t,
                            typename std::conditional<K <= UINT16_MAX,
                            std::uint16_t,
                            std::uint32_t>::type>::type u_;
};

int main()
{
  A<100> small;
  A<1000> medium;
  A<100000> large;

  assert( (std::is_same<std::uint8_t, decltype(small.u_)>::value) );
  assert( (std::is_same<std::uint16_t, decltype(medium.u_)>::value) );
  assert( (std::is_same<std::uint32_t, decltype(large.u_)>::value) );
}
Run Code Online (Sandbox Code Playgroud)

这假定:

  • uint8_ttemplate<uint8_t k, typename >仅仅是一个疏忽,或如指出亚伦McDaid的评论,示例不起作用.我改变uint8_tuint32_t(该示例可以平滑地延伸到uint64_t);
  • int k_ = k;是一个类内成员初始化.对于常量定义,您可以使用enum {k_ = K};
  • u_in typename u_;是一个数据成员.如果它是类型定义,您可以使用a typedeftype-alias(C++ 11)轻松更改示例.

如果您不能使用C++ 11,那么可以使用boost :: conditional或者您可以编写自己的版本:

template<bool, class T, class F>
struct conditional { typedef T type; };

template<class T, class F>
struct conditional<false, T, F> { typedef F type; }; 
Run Code Online (Sandbox Code Playgroud)


Bar*_*rry 6

如果我们想要的是:给定一个uint64_t模板参数,给出能够表示它的最小无符号类型,那么我们真正想要的只是编译时的简单迭代。

namespace details {
    template <typename T>
    struct tag {
        using type = T;
    };

    // base case: just fail
    template <uint64_t V, typename... >
    struct min_unsigned_type;

    // recursive case: check using numeric_limits
    template <uint64_t V, typename T, typename... Ts>
    struct min_unsigned_type<V, T, Ts...>
    : std::conditional_t<(V <= std::numeric_limits<T>::max()),
                         tag<T>,
                         min_unsigned_type<V, Ts...>>
    { };
}
Run Code Online (Sandbox Code Playgroud)

然后只是将事物包装在一起的别名:

template <uint64_t V>
using min_unsigned_type = 
    typename details::min_unsigned_type<V, 
        uint8_t, uint16_t, uint32_t, uint64_t>::type;
Run Code Online (Sandbox Code Playgroud)

这具有额外的优势,即能够轻松指定您想要走多远,或者如果您认为有必要,甚至能够添加更大的无符号类型。

最后你的班级:

template <uint64_t V>
struct A {
    static constexpr uint64_t k_ = V;
    min_unsigned_type<V> u_;
};
Run Code Online (Sandbox Code Playgroud)


son*_*yao 0

您可以对部分绑定的模板使用 using 声明:

template<uint8_t k>
using A_uint8 = A<k, uint8_t>;

template<uint8_t k>
using A_uint16 = A<k, uint16_t>;
Run Code Online (Sandbox Code Playgroud)

然后:

A_uint8<7> hello; // -> A<7, uint8_t>;
A_uint16<256> hello; // -> A<256, uint16_t>;
Run Code Online (Sandbox Code Playgroud)