2015-02-10 63 views
5

在可變參數模板類型列表(參數包)中實現基於索引的插入和刪除類型的最佳方式是什麼?插入/刪除類型到可變參數模板列表(參數包)

所需的代碼/行爲:

template<typename...> struct List { /* ... */ }; 

static_assert(is_same 
< 
    List<int, char, float>::Insert<int, 0>, 
    List<int, int, char, float> 
>()); 

static_assert(is_same 
< 
    List<int, char, float>::Insert<int, 2>, 
    List<int, char, int, float> 
>()); 

static_assert(is_same 
< 
    List<int, char, float>::Remove<0>, 
    List<char, float> 
>()); 

static_assert(is_same 
< 
    List<int, char, float>::Remove<1>, 
    List<int, float> 
>()); 

我試圖實現基於在最初空單推回參數,但它是非常難讀/維持。該參數是與此類似:

template<typename T, int I, int ITarget, typename TResult> struct InsertImpl; 

我不斷遞增I直到它等於ITarget,推背現有各類在TResult,這是一個List<...>。當I等於ITarget時,我也推回TTResult

刪除一個類型有一個類似的實現 - 而不是在索引相等時推回兩次,我簡單地跳過了類型。

我的繁瑣的解決方案將實現推入和彈出方面的插入和刪除。我相信,推到前面等於Insert<0>並且推回到等於Insert<size>會更優雅。這同樣適用於從前面和後面彈出。

有沒有更好的方法來做到這一點? C++ 14功能可以幫助嗎?

+1

如何使用'boost :: mpl :: vector'來幫助完成這些操作? – Pradhan 2015-02-10 15:05:22

回答

1

由於你提到了C++ 14,這裏是另一個使用std::index_sequence。我認爲解決方案值得一提的主要原因是使用映射函數constexpr將這些類型放在結果List中的位置上。這使得實現相對簡單。

#include <cstddef> 
#include <tuple> 
#include <utility> 

template<typename...> struct List; 

constexpr std::size_t map_ins(std::size_t i, std::size_t from, std::size_t to) 
{ 
    return i < to ? i : i == to ? from : i - 1; 
} 

template<typename, typename, std::size_t, typename...> struct ins_hlp; 

template<std::size_t... Is, typename U, std::size_t N, typename... Ts> 
struct ins_hlp<std::index_sequence<Is...>, U, N, Ts...> 
{ 
    static_assert(N <= sizeof...(Ts), "Insert index out of range"); 
    using type = List<std::tuple_element_t<map_ins(Is, sizeof...(Ts), N), std::tuple<Ts..., U>>...>; 
}; 

constexpr std::size_t map_rem(std::size_t i, std::size_t idx) 
{ 
    return i < idx ? i : i + 1; 
} 

template<typename, std::size_t, typename...> struct rem_hlp_2; 

template<std::size_t... Is, std::size_t N, typename... Ts> 
struct rem_hlp_2<std::index_sequence<Is...>, N, Ts...> 
{ 
    using type = List<std::tuple_element_t<map_rem(Is, N), std::tuple<Ts...>>...>; 
}; 

template<std::size_t N, typename... Ts> struct rem_hlp 
{ 
    static_assert(N < sizeof...(Ts), "Remove index out of range"); 
    using type = typename rem_hlp_2<std::make_index_sequence<sizeof...(Ts) - 1>, N, Ts...>::type; 
}; 

template<typename... Ts> struct List 
{ 
    template<typename U, std::size_t N> using Insert = typename ins_hlp<std::make_index_sequence<sizeof...(Ts) + 1>, U, N, Ts...>::type; 
    template<std::size_t N> using Remove = typename rem_hlp<N, Ts...>::type; 
}; 

對不起,我沒有找到另一種有意義的方式來格式化這些參數列表。

Remove額外的助手的唯一原因是邊界檢查;如果不需要,Remove可以使用與Insert相同的模式。

+0

非常乾淨和有趣的解決方案。我對這種實現並不是很熟悉,但我會盡我所能詳細瞭解它。謝謝! – 2015-02-12 18:09:05

3

不知道有任何 「最佳」 的方式,但是這是一個非遞歸的方式:

#include <utility> 
#include <type_traits> 
#include <tuple> 

template<typename...Ts> struct List; 

template<typename T> struct ListFromTupleImpl; 
template<typename...Ts> 
struct ListFromTupleImpl<std::tuple<Ts...>> 
{ using type = List<Ts...>; }; 

template<typename T> 
using ListFromTuple = typename ListFromTupleImpl<T>::type; 

template<typename...Ts> 
using TupleCat = decltype(std::tuple_cat(std::declval<Ts>()...)); 

template<typename...Ts> 
using ListFromTupleCat = ListFromTuple<TupleCat<Ts...>>; 

template<unsigned P,typename T,typename I> struct RemoveFromListImpl; 
template<unsigned P,typename...Ts,std::size_t...Is> 
struct RemoveFromListImpl<P,List<Ts...>,std::index_sequence<Is...>> 
{ 
    using type = ListFromTupleCat< 
     std::conditional_t<(Is==P),std::tuple<>,std::tuple<Ts>>...>; 
}; 

// All elements < P 
template<unsigned P,typename T,typename I> struct HeadImpl; 
template<unsigned P,typename...Ts,std::size_t...Is> 
struct HeadImpl<P,List<Ts...>,std::index_sequence<Is...>> 
{ 
    using type = TupleCat< 
     std::conditional_t<(Is>=P),std::tuple<>,std::tuple<Ts>>...>; 
}; 

// All elements >= P 
template<unsigned P,typename T,typename I> struct TailImpl; 
template<unsigned P,typename...Ts,std::size_t...Is> 
struct TailImpl<P,List<Ts...>,std::index_sequence<Is...>> 
{ 
    using type = TupleCat< 
     std::conditional_t<(Is<P),std::tuple<>,std::tuple<Ts>>...>; 
}; 

template<typename N,unsigned P,typename T,typename I> 
struct InsertIntoListImpl 
{ 
    using head = typename HeadImpl<P,T,I>::type; 
    using tail = typename TailImpl<P,T,I>::type; 
    using type = ListFromTupleCat<head,std::tuple<N>,tail>; 
}; 

template<typename...Ts> struct List { 
    /* ... */ 
    template<std::size_t P> 
    using Remove = 
     typename RemoveFromListImpl<P,List<Ts...>, 
     std::index_sequence_for<Ts...>>::type; 

    template<typename N,std::size_t P> 
    using Insert = 
     typename InsertIntoListImpl<N,P,List<Ts...>, 
     std::index_sequence_for<Ts...>>::type; 
}; 


static_assert(std::is_same 
< 
    List<int, char, float>::Remove<0>, 
    List<char, float> 
>(), ""); 

static_assert(std::is_same 
< 
    List<int, char, float>::Remove<1>, 
    List<int, float> 
>(), ""); 

static_assert(std::is_same 
< 
    List<int, char, float>::Insert<int, 0>, 
    List<int, int, char, float> 
>(), ""); 

static_assert(std::is_same 
< 
    List<int, char, float>::Insert<int, 2>, 
    List<int, char, int, float> 
>(), ""); 

int main(){} 

Live example

1

使用Eric Niebler's Tiny Meta-Programming LibraryDEMO):

template <std::size_t N, typename List> 
using take_c = 
    meta::reverse< 
    meta::drop_c< 
     meta::size<List>::value - N, 
     meta::reverse<List> 
>>; 

template <typename...Ts> struct List { 
    using mlist = meta::list<Ts...>; 

    template <typename T, std::size_t I> 
    using Insert = 
    meta::apply_list< 
     meta::quote<::List>, 
     meta::concat< 
     take_c<I, mlist>, 
     meta::list<T>, 
     meta::drop_c<I, mlist> 
    >>; 

    template <std::size_t I> 
    using Remove = 
    meta::apply_list< 
     meta::quote<::List>, 
     meta::concat< 
     take_c<I, mlist>, 
     meta::drop_c<I + 1, mlist> 
    >>; 
}; 
相關問題