2017-09-13 104 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