2017-09-13 112 views
1

我有一个具有完整模板参数N的类。C++依赖于模板类型的数组类型

template<unsigned int N> 
class test { 
} 

现在我想有一个std::vector与整型是尽可能小以保持N位。

例如

class test<8> { 
    std::vector<uint8_t> data; 
} 

class test<9> { 
    std::vector<uint16_t> data; 
} 

class test<10> { 
    std::vector<uint16_t> data; 
} 
... 

有没有更好的方式来为N=1N=64做到这一点?

+0

这种“向量”的用例是什么? ['std :: bitset'](http://en.cppreference.com/w/cpp/utility/bitset)能解决你的问题吗? –

+0

我想排序生成的向量,所以我认为bitset将不可行。 – eclipse

+0

为了给出更多的上下文:它是一个代表数十亿字符串的类,其固定长度来自4个字符(基因组)的字母表。 N是字符数,最大值是32.字母4中的32个字符可以用64位表示。所以载体基本上是很多小的基因组样本,这些样本的大小应该是通用的。 – eclipse

回答

3

如何使用条件?

#include <vector> 
#include <cstdint> 
#include <iostream> 
#include <type_traits> 

template <std::size_t N> 
struct foo 
{ 
    static_assert(N < 65U, "foo 64 limit"); 

    using vType = typename std::conditional< 
     (N < 9U), std::uint8_t, 
     typename std::conditional< (N < 17U), std::uint16_t, 
      typename std::conditional< (N < 33U), std::uint32_t, std::uint64_t 
    >::type>::type>::type; 

    std::vector<vType> data; 
}; 

int main() 
{ 
    static_assert(1U == sizeof(foo<1>::vType), "!"); 
    static_assert(1U == sizeof(foo<8>::vType), "!"); 
    static_assert(2U == sizeof(foo<9>::vType), "!"); 
    static_assert(2U == sizeof(foo<16>::vType), "!"); 
    static_assert(4U == sizeof(foo<17>::vType), "!"); 
    static_assert(4U == sizeof(foo<32>::vType), "!"); 
    static_assert(8U == sizeof(foo<33>::vType), "!"); 
    static_assert(8U == sizeof(foo<64>::vType), "!"); 

    // foo<65> f65; compilation error 
} 

或者,在一个更优雅的方式(恕我直言),你可以定义一个类型的特征(selectTypeByDim,在下面的例子),在列表中选择第一个有用的类型基于

#include <tuple> 
#include <vector> 
#include <cstdint> 
#include <climits> 
#include <type_traits> 

template <std::size_t N, typename T, 
    bool = (N <= sizeof(typename std::tuple_element<0U, T>::type)*CHAR_BIT)> 
struct stbdH; 

template <std::size_t N, typename T0, typename ... Ts> 
struct stbdH<N, std::tuple<T0, Ts...>, true> 
{ using type = T0; }; 

template <std::size_t N, typename T0, typename ... Ts> 
struct stbdH<N, std::tuple<T0, Ts...>, false> 
{ using type = typename stbdH<N, std::tuple<Ts...>>::type; }; 

template <std::size_t N, typename ... Ts> 
struct selectTypeByDim : stbdH<N, std::tuple<Ts...>> 
{ }; 

template <std::size_t N> 
struct foo 
{ 
    static_assert(N < 65U, "foo 64 limit"); 

    using vType = typename selectTypeByDim<N, 
     std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t>::type; 

    std::vector<vType> data; 
}; 

int main() 
{ 
    static_assert(1U == sizeof(foo<1U>::vType), "!"); 
    static_assert(1U == sizeof(foo<CHAR_BIT>::vType), "!"); 
    static_assert(2U == sizeof(foo<CHAR_BIT+1U>::vType), "!"); 
    static_assert(2U == sizeof(foo<(CHAR_BIT<<1)>::vType), "!"); 
    static_assert(4U == sizeof(foo<(CHAR_BIT<<1)+1U>::vType), "!"); 
    static_assert(4U == sizeof(foo<(CHAR_BIT<<2)>::vType), "!"); 
    static_assert(8U == sizeof(foo<(CHAR_BIT<<2)+1U>::vType), "!"); 
    static_assert(8U == sizeof(foo<(CHAR_BIT<<3)>::vType), "!"); 

    //foo<(CHAR_BIT<<3)+1U> f65; compilation error 
} 
+0

这是完美的感谢,非常美丽。 – eclipse

1

对你的问题的评论,你并不需要一个内置的类型。任何可复制的可移动对象都可以放入矢量中。

如果你想要的数据做的是排序的话,我会建议,要么std::bitset<2*N>std::array<std::byte, (2*N+7)/8>(或者,如果你一个奇怪的系统上运行std::array<std::byte, (2*N+CHAR_BIT-1)/CHAR_BIT>)定制Compare排序。如果您有任何其他用途(您可能会这样做),我会推荐一个自定义类,并将其中的一个用作其基础存储;这可以让您添加更多功能,如获取特定的基础。

在大多数系统上,如果N是32或64(取决于实现)的倍数,则std::bitset将不会有任何开销,但不能保证。一般来说,字节数组的开销不会超过C++所要求的开销。如果有必要,这两种扩展都可以扩展到64位以上,并且字节数组的空间开销最小,比使用整型更少,因为它不会强制大小为2的幂。在其中任何一个上面添加自定义类都不会产生任何开销。

(如果使用的是C++ 14或以下,您可以使用charuint8_t,而不是byte;一个byte是更接近你想要什么,但只在C++ 17可如果你使用C +现在+14,但可能会切换,我建议将using byte = uint8_t放在代码的某个地方,然后在升级时将其切换出来)

+0

我实际上首先使用了字节数组的解决方案。但我认为我有一件错误的事情让我考虑其他选择。当我有一个只有一个字段的类'std :: array '该类中的对象如何在向量内部放置?我今天读到了'放置新的'。但我并不十分确定在这种情况下它将如何工作。你说它不会增加开销,怎么样? – eclipse

+1

一个向量具有一组连续布局的对象,就像一个数组,但是在运行时创建。一个类与其每个成员一个接一个地排列,也许在成员之间有一些填充或者为了对齐目的而在结尾处。由于它只有一个数据成员,所以不会超过使用它的额外对齐要求,并且除了数据成员将是'byte',无论如何它都没有对齐要求,所以不会有填充。 –

+0

嗯好吧我看到它是如何工作的,像这样'std :: byte []'但是'std :: array'有多个成员将会在向量中?! – eclipse