模板中的无限递归

时间:2016-05-31 01:47:07

标签: c++ templates recursion template-meta-programming

假设我有一个名为List的代理类,它只不过是一堆typename的持有者:

template<typename... items> class List { 
  constexpr size_t SizeOf = /*Magic code that determines the length*/;
};

假设我有另一个课程应该带到Lists并输出一个版本,其中较小的一个用null_t s填充:

template<class flist,class slist>
class Pad{
  typedef /*Undertermined*/ Flist;
  typedef /*Undertermined*/ Slist;
};

唯一真正的问题是打破递归....通常在模板递归中你只需要专门化,然后就可以结束了。

这里有点不同,因为没有办法(至少我可以看到)通过模板减速来判断两个列表之间的差异。

我尝试使用std::conditional来结束循环,但这不起作用。

以下是一个例子:

template<int x>
class Mine{
  typedef std::conditional<x == 12, Mine<x>::value, Mine<x+1>::value> value;
};

即使我有x==12条件,它仍然需要(或想要)清除Mine<x+1>::value

那么这种情况的一般策略是什么?

3 个答案:

答案 0 :(得分:1)

在第二个示例中停止递归的一种方法是使用boost.mpl。例如,

#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/integral_c.hpp>

template<int x>
struct Mine {
  using type = typename boost::mpl::eval_if<boost::mpl::bool_<x == 12>,
                                   boost::mpl::integral_c<short, 12>,
                                   Mine<x+1>
                                  >::type;
};

在上面的例子中,无论参数如何,它总是会给你12作为值(只要它小于或等于12)。

答案 1 :(得分:1)

这是Pad实现的非递归解决方案,但它也展示了如何使用部分特化来避免根据条件实例化模板:

#include <cstddef>
#include <utility>
#include <type_traits>
#include <iostream>

template<class... Ts> struct List { static constexpr std::size_t size = sizeof...(Ts); };
struct null_t { };

template<std::size_t> using make_null_t = null_t;

template<class, class> struct pad_imp2;
template<class... Ts, std::size_t... Is> struct pad_imp2<List<Ts...>, std::index_sequence<Is...>>
{
   using type = List<Ts..., make_null_t<Is>...>;
};

// Don't instantiate make_index_sequence if L::size >= S.
template<class, std::size_t, bool> struct pad_imp { using type = void; };
template<class L, std::size_t S> struct pad_imp<L, S, true>
{
   using type = typename pad_imp2<L, std::make_index_sequence<S - L::size>>::type;
};

template<class L, std::size_t S> using pad_hlp = typename pad_imp<L, S, L::size < S>::type;

template<class L1, class L2> struct Pad
{
   using L1_padded = std::conditional_t<L1::size < L2::size, pad_hlp<L1, L2::size>, L1>;
   using L2_padded = std::conditional_t<L2::size < L1::size, pad_hlp<L2, L1::size>, L2>;
};

int main()
{
   using list1 = List<short, int, long>;
   using list2 = List<double>;
   std::cout << std::is_same<Pad<list1, list2>::L1_padded, list1>::value << '\n';
   std::cout << std::is_same<Pad<list1, list2>::L2_padded, List<double, null_t, null_t>>::value << '\n';
}

填充是通过使用虚拟null_t别名模板和一组正确大小的索引生成make_null_t列表来完成的。

答案 2 :(得分:0)

据我了解,你需要打破Mine结构的递归。如果这是正确的,您可以使用此结构的完整模板特化(用于实现递归的终端情况)。它看起来像是:

template<>
struct Mine<123456>{
  // It is struct represents end of recursion
};

这是用于计算阶乘的简单示例:

#include <iostream>

template <int N>
struct Factorial
{
   static const int fact = N * Factorial<N - 1>::fact;
};

template <>
struct Factorial<0>
{
    static const int fact = 1;
};

int main()
{
    std::cout << Factorial<1>::fact << std::endl;
    std::cout << Factorial<3>::fact << std::endl;
    return 0;
}