Insert/remove 键入可变参数模板列表(参数包)
Insert/remove type into variadic template list (parameter pack)
在可变参数模板类型列表(参数包)中实现基于索引的类型插入和删除的最佳方法是什么?
想要code/behavior:
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>
>());
我尝试了一种基于推回初始为空列表中的参数的实现,但是很难 read/maintain。参数类似于:
template<typename T, int I, int ITarget, typename TResult> struct InsertImpl;
我不断递增 I
直到它等于 ITarget
,推回 TResult
中的现有类型,即 List<...>
。当 I
等于 ITarget
时,我也在 TResult
中推回 T
。
删除一个类型有一个类似的实现 - 而不是在索引相等时推回两次,我只是跳过了该类型。
我笨拙的解决方案是在推入和弹出方面实现插入和移除。我相信推到前面等于 Insert<0>
推到后面等于 Insert<size>
会更优雅。同样适用于从前面和从后面弹出。
有更好的方法吗? C++14 特性有帮助吗?
不确定是否有任何 "best" 方式,但这是一种非递归方式:
#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(){}
使用Eric Niebler's Tiny Meta-Programming Library (DEMO):
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>
>>;
};
既然你提到了 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
.
相同的模式
在可变参数模板类型列表(参数包)中实现基于索引的类型插入和删除的最佳方法是什么?
想要code/behavior:
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>
>());
我尝试了一种基于推回初始为空列表中的参数的实现,但是很难 read/maintain。参数类似于:
template<typename T, int I, int ITarget, typename TResult> struct InsertImpl;
我不断递增 I
直到它等于 ITarget
,推回 TResult
中的现有类型,即 List<...>
。当 I
等于 ITarget
时,我也在 TResult
中推回 T
。
删除一个类型有一个类似的实现 - 而不是在索引相等时推回两次,我只是跳过了该类型。
我笨拙的解决方案是在推入和弹出方面实现插入和移除。我相信推到前面等于 Insert<0>
推到后面等于 Insert<size>
会更优雅。同样适用于从前面和从后面弹出。
有更好的方法吗? C++14 特性有帮助吗?
不确定是否有任何 "best" 方式,但这是一种非递归方式:
#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(){}
使用Eric Niebler's Tiny Meta-Programming Library (DEMO):
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>
>>;
};
既然你提到了 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
.