模板中的无限递归

Infinite recursion in templates

假设我有一个名为 List 的代理 class,它只不过是一堆 typenames:

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

假设我有另一个 class 应该带到 Lists 并输出一个版本,其中较小的版本用 null_ts:

填充
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

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

在第二个示例中停止递归的一种方法是使用 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 作为值。

据我了解,您需要在 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;
}

这是实现 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';
}

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