2016-11-30 60 views
1

如果我有一個包含N個大小相等的vectors的類。我將如何去實現一個標準的迭代器模板,它將在1到N個矢量之間迭代。我寫了一個小例子來說明問題。如何爲其中包含N個向量的類實現子集迭代器

#include <bitset> 
#include <tuple> 
#include <type_traits> 
#include <vector> 

//Since std::get<>() for types isn't in c++11, I use this meta-function to determine the index 
//of a Type in a list of Types, starting from 0. ex: IndexOf<C, A, B, C>::value = 2 
template <typename T, typename... Ts> 
struct IndexOf; 

template <typename T, typename... Ts> 
struct IndexOf<T, T, Ts...> : std::integral_constant<std::size_t, 0> {}; 

template <typename T, typename U, typename... Ts> 
struct IndexOf<T, U, Ts...> : std::integral_constant<std::size_t, 1 + IndexOf<T, Ts...>::value> {}; 

//Used to determine the slot we're interesting in. 
using Handle = const std::size_t; 

template<typename... Types> 
class DataManager 
{ 
    static constexpr std::size_t TypeCount = sizeof... (Types); 
    using Flags = std::bitset<TypeCount>; //BitMask to determine if the handle has a certain piece of data initialized 

    std::size_t count, capacity; 
    std::tuple<std::vector<Types>..., std::vector<Flags>> vectors; //Tuple of vectors, holding the types and flags. 
public: 
    DataManager(std::size_t n) : count(0), capacity(n), 
    vectors(std::make_tuple(std::vector<Types>(n)..., std::vector<Flags>(n))) 
    {} 

    template <typename Type, typename... Args> 
    void add(Handle handle, Args&&... args) { //Initializes the type in the handle slot of the vector 
     Flags& flags = std::get<TypeCount>(vectors)[handle]; //Flag the bit, notify that handle 
     flags.set(IndexOf<Type, Types...>::value);    //has that piece of data initialized 

     std::get<IndexOf<Type, Types...>::value>(vectors)[handle] = Type{ args... }; 
    } 

    template <typename Type> 
    Type& get(Handle handle) { //Returns the Type in handle slot of the vector 
     return std::get<IndexOf<Type, Types...>::value>(vectors)[handle]; 
    } 

    template <typename Type> 
    bool has(Handle handle) { //Returns true if the Type is initialized, by checking the bitset 
     Flags& flags = std::get<TypeCount>(vectors)[handle]; 
     return flags.test(IndexOf<Type, Types...>::value); 
    } 

    Handle push_back() { 
     return count++; 
    } 
}; 

其中我目前使用這樣的訪問數據:

//Simple Data 
struct D0 { int x, y; }; 
struct D1 { float n, m; }; 
struct D2 { int x, y, z; }; 

int main() 
{ 
    DataManager<D0, D1, D2> manager(100); 
    Handle h0 = manager.push_back(); 

    std::cout << manager.has<D0>(h0) << std::endl; //prints false, h0 doesn't have D0 initialized 
    manager.add<D0>(h0, 75, 20);      //initialize D0 for h0 

    std::cout << manager.has<D0>(h0) << std::endl; //prints ture, h0 is now initialzed 
    std::cout << manager.get<D0>(h0).x << std::endl; //prints 75 
} 

我怎麼可以添加迭代器功能的DataManager類,那隻能遍歷這樣選擇的數據?

int main() 
{ 
    ... 
    for (D0 d1, D3 d3 : manager) { 
     ... //Iterate over all D0s and D3s between 0 and count 
    } 
    //or 
    for(DataManager<D0>::iterator it = v.begin(); it != v.end(); ++it { 
     ... //Iterate over just D0s between 0 and count - 10 
    } 
} 
+0

如何處理沒有初始化的「缺失」數據(通過標記?)如果缺少的元素在迭代的範圍內不均勻,你會怎麼做?你見過boost zip迭代器嗎?我猜你只需要'(:)'基於範圍的循環迭代? – Yakk

+0

對不起,沒有解釋「缺少」數據的行爲。我想跳過那個插槽。我希望'for(:)'爲基礎的循環,併爲完整for循環的迭代器成員。 – Brian

回答

3

編寫一個範圍視圖類型。範圍視圖有兩個迭代器,一個開始和一個結束,並公開.begin().end()。返回迭代器的範圍視圖可以讓你做一個for(:)循環而不需要複製任何東西。

接下來,您需要一個覆蓋選定元素的迭代器。我可以想到兩種方法。

首先,一個zip迭代器。一個zip迭代器有一個迭代器的元組,並且它並行地推進它們。當您解除引用時,它將返回每個迭代器的解引用的std::tie

第二個選項,一個生成器迭代器。生成器迭代器有一個索引和一個從索引映射到某種類型的函數。 ++==等只是提前/比較索引。 *返回函數調用的結果。在這種情況下,您將從該函數返回一個tie

我通常通過與分度迭代開始(存儲一個索引,並且*返回它的一個副本),然後寫一個轉換迭代(存儲一個迭代,向前==++等以教學貫徹發電機迭代它存儲函數f,並且*確實是f(*it),其中it是存儲的迭代器)。生成器迭代器現在只是一個transform_iterator<F(index_iterator)>


這些都有效地限制於滿足一個輸入迭代的公理,由於周圍::reference型等的規則。但是,輸入迭代器足夠執行for(:)循環;更何況,for(:)循環甚至不需要合法的迭代器,因爲它是用代碼而不是迭代器語義來定義的。


生成器/轉換迭代器已經足夠,並且在其他上下文中都有用,所以我會採用這種方法。

如果需要,您可以選擇鍵入 - 擦除轉換(到std::function< T(std::size_t) >)。

template<class T> 
using any_generator_iterator = transform_iterator< std::function<T(std::size_t)>(indexing_iterator) >; 

現在在種迭代A B和C,我們做到:

template<class...Ts, 
    class R=std::tuple<Ts&...>, 
    class It=any_generator_iterator<R> 
> 
range_view<It> 
iterate_over() { 
    auto get = [this](std::size_t i)->R { 
    return std::tie(this->get<Ts>()...); 
    }; 
    return { {get, 0}, {{}, count} }; 
} 

別處:

for(auto i : foo.iterate_over<A,B,C>()) { 
    auto&& a = std::get<0>(i); 
    auto&& b = std::get<1>(i); 
    auto&& c = std::get<2>(i); 
    // code 
} 

有一堆在它上面庫代碼。

大部分(如果不是全部的話)已經在boost中解決了。

相關問題