如何使用参数包在C ++ 11中实现boost MPL FOLD?

时间:2013-11-18 18:44:12

标签: c++ boost c++11 variadic-templates boost-mpl

boost mpl有更常见的算法 - fold。这个算法是许多其他算法的基础。

template< typename Seq, typename State, typename Op> struct fold { ... }
//Here Seq is <T0,T1,...,Tn> any sequence.
// result of fold is  op<  op < ...< op<State,T0>::type, T1>::type > ... >, Tn>::type

了解更多信息,请阅读link

带有可变参数模板的c ++ 11中的

Fold可能会重新定义如下:

 template< typename state, typename op, typename ...elements> struct fold;

如何实现它不是问题,但如何仅使用参数实现它 包装很难或可能是不可解决的问题。

问:是否可以仅使用参数打包实现?

我想要像

这样的东西
template< typename OP, typename State, typename ...T>
struct fold
{
     // only for illustration
     typedef  apply< apply<....<apply<Op,State,T>>...>::type type; 
};

2 个答案:

答案 0 :(得分:3)

这是对对数模板递归深度fold实现的一个刺。

#include <cstddef>

template<typename... Ts> struct types {};
template<typename T, typename U>
struct concat;
template<typename... Ts, typename... Us>
struct concat< types<Ts...>, types<Us...> > {
  typedef types<Ts..., Us...> result;
};
template<typename Ts, typename Us>
using Concat = typename concat<Ts, Us>::result;

template<std::size_t n, typename Ts>
struct split;
template<std::size_t n, typename... Ts>
struct split<n, types<Ts...>> {
private:
  typedef split<n/2, types<Ts...>> one;
  typedef split<n-n/2, typename one::right> two;
public:
  typedef Concat< typename one::left, typename two::left > left;
  typedef typename two::right right;
};
template<typename... Ts>
struct split<0, types<Ts...>> {
  typedef types<> left;
  typedef types<Ts...> right;
};
template<typename T, typename... Ts>
struct split<1, types<T, Ts...>> {
  typedef types<T> left;
  typedef types<Ts...> right;
};
template<template<typename, typename>class OP, typename State, typename Ts>
struct fold_helper;
template<template<typename, typename>class OP, typename State, typename... Ts>
struct fold_helper<OP, State, types<Ts...>> {
private:
  typedef split<sizeof...(Ts)/2, types<Ts...>> parts;
  typedef typename parts::left left;
  typedef typename parts::right right;
  typedef typename fold_helper<OP, State, left>::result left_result;
public:
  typedef typename fold_helper<OP, left_result, right>::result result;
};
template<template<typename, typename>class OP, typename State>
struct fold_helper<OP, State, types<>> {
  typedef State result;
};
template<template<typename, typename>class OP, typename State, typename T>
struct fold_helper<OP, State, types<T>> {
  typedef typename OP<State,T>::type result;
};
template<template<typename, typename>class OP, typename State, typename... Ts>
struct fold {
  typedef typename fold_helper<OP, State, types<Ts...>>::result type;
};
template<template<typename, typename>class OP, typename State, typename... Ts>
using Fold = typename fold<OP, State, Ts...>::type;

template<typename left, typename right>
struct op_test {
  typedef int type;
};

int main() {
  Fold< op_test, double, int, char, char*, int* > foo = 8;
}

这里我首先使用线性列表,然后使用split元函数将其分成两个半长列表。

一旦我有两半,我在上半场fold,然后取得结果,fold超过下半场。

当O(N)工作完成时,你在任何时候都只有O(lg(N))。

我没有看到理论上的原因,为什么我们不能拉出O(lg(lg(N))深度,但我也没有看到这一点:最大深度为~1000,你必须有几十个类型列表上的嵌套fold长度超出模板堆栈空间:根据我的经验,编译器在达到对数深度限制之前很久就会爆炸。

现在它编译:http://ideone.com/CdKAAT

split是一种处理长列表的通用方法,每个元素都不会递归一次。一旦掌握了对数foldfold_helpersplit就非常明显了。

答案 1 :(得分:1)

为什么仅对参数包装进行限制?

与许多参数包解决方案一样,您可能不得不深入研究某种重载或部分特化。鉴于你正在编写模板元函数,我会选择部分专业化。

下面通过返回状态来处理具有0个元素的情况,并且我们在模板中使用该位置来累积结果。专门化处理1+案例,通过将第一个元素与状态组合,并递归调用模板元函数。

template< typename OP, typename State, typename ...T>
struct fold
{
    typedef State type;
};

template<typename OP, typename State, typename Head, typename... Tail>
struct fold< OP, State, Head, Tail...>
{
   typedef typename fold<OP, typename OP::template apply<State,Head>::type, Tail...>::type type;
};

http://ideone.com/DDCCbB