折叠任意多个可变参数包
Folding over arbitrarily many variadic packs
我正在阅读 Eric Niebler 的 post 关于他的小型元编程库。在尝试实现他省略/列为挑战的部分时,我只剩下 transform
的以下实现:
template <typename F, typename... As>
using meta_apply = typename F::template apply<As...>;
template <typename... >
struct typelist_transform;
// unary
template <typename... T, typename F>
struct typelist_transform<typelist<T...>, F>
{
using type = typelist<meta_apply<F,T>...>;
};
// binary
template <typename... T, typename... U, typename F>
struct typelist_transform<typelist<T...>, typelist<U...>, F>
{
using type = typelist<meta_apply<F,T,U>...>;
};
这行得通,但对我来说似乎非常不满意 - 我需要将 typelist_transform
的每个数字 "arguments" 部分特化为 F
。有没有更好的方法来实现这个元函数?
接口略有修改(首先使用元函数,而不是最后一个,并将几个成员添加到 typelist
以获得其大小和第 N 个成员,而不是使用独立的元函数):
template <typename... Ts>
struct typelist {
template<size_t N>
using get = std::tuple_element_t<N, std::tuple<Ts...>>;
static constexpr size_t size = sizeof...(Ts);
};
template<class, class, class...>
struct typelist_transform_helper;
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename Ls::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
template<class F, class L1, class... Lists>
struct typelist_transform : typelist_transform_helper<std::make_index_sequence<L1::size>, F, L1, Lists...> {
// static assert on size of all lists being equal if wanted
};
Demo.
上面的一个问题是 tuple_element
通常需要 N
递归模板实例化,使得模板实例化的总数在列表的长度上成二次方。鉴于我们的用例(我们访问每个索引),我们可以做得更好:
template<class L>
struct typelist_indexer {
template<size_t N, class T> struct helper_base { using type = T; };
template<class S, class> struct helper;
template<size_t... Ns, class... Ts>
struct helper<std::index_sequence<Ns...>, typelist<Ts...>> : helper_base<Ns, Ts>... {};
template<size_t N, class T>
static helper_base<N, T> do_get(helper_base<N, T>);
using helper_type = helper<std::make_index_sequence<L::size>, L>;
template<size_t N>
using get = typename decltype(do_get<N>(helper_type()))::type;
};
然后
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename typelist_indexer<Ls>::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
现在模板实例化的数量与列表的大小成线性关系,在我的测试中这导致编译时间显着改善。 Demo.
我正在阅读 Eric Niebler 的 post 关于他的小型元编程库。在尝试实现他省略/列为挑战的部分时,我只剩下 transform
的以下实现:
template <typename F, typename... As>
using meta_apply = typename F::template apply<As...>;
template <typename... >
struct typelist_transform;
// unary
template <typename... T, typename F>
struct typelist_transform<typelist<T...>, F>
{
using type = typelist<meta_apply<F,T>...>;
};
// binary
template <typename... T, typename... U, typename F>
struct typelist_transform<typelist<T...>, typelist<U...>, F>
{
using type = typelist<meta_apply<F,T,U>...>;
};
这行得通,但对我来说似乎非常不满意 - 我需要将 typelist_transform
的每个数字 "arguments" 部分特化为 F
。有没有更好的方法来实现这个元函数?
接口略有修改(首先使用元函数,而不是最后一个,并将几个成员添加到 typelist
以获得其大小和第 N 个成员,而不是使用独立的元函数):
template <typename... Ts>
struct typelist {
template<size_t N>
using get = std::tuple_element_t<N, std::tuple<Ts...>>;
static constexpr size_t size = sizeof...(Ts);
};
template<class, class, class...>
struct typelist_transform_helper;
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename Ls::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
template<class F, class L1, class... Lists>
struct typelist_transform : typelist_transform_helper<std::make_index_sequence<L1::size>, F, L1, Lists...> {
// static assert on size of all lists being equal if wanted
};
Demo.
上面的一个问题是 tuple_element
通常需要 N
递归模板实例化,使得模板实例化的总数在列表的长度上成二次方。鉴于我们的用例(我们访问每个索引),我们可以做得更好:
template<class L>
struct typelist_indexer {
template<size_t N, class T> struct helper_base { using type = T; };
template<class S, class> struct helper;
template<size_t... Ns, class... Ts>
struct helper<std::index_sequence<Ns...>, typelist<Ts...>> : helper_base<Ns, Ts>... {};
template<size_t N, class T>
static helper_base<N, T> do_get(helper_base<N, T>);
using helper_type = helper<std::make_index_sequence<L::size>, L>;
template<size_t N>
using get = typename decltype(do_get<N>(helper_type()))::type;
};
然后
template<size_t... Ints, class F, class...Lists>
struct typelist_transform_helper<std::index_sequence<Ints...>, F, Lists...>{
template <class MF, size_t N, typename... Ls>
using apply_to_Nth = meta_apply<MF, typename typelist_indexer<Ls>::template get<N>...>;
using type = typelist<apply_to_Nth<F, Ints, Lists...>...>;
};
现在模板实例化的数量与列表的大小成线性关系,在我的测试中这导致编译时间显着改善。 Demo.