【问题标题】:Infinite recursion in templates模板中的无限递归
【发布时间】:2016-05-31 01:47:06
【问题描述】:

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

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

假设我有另一个类应该带到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&lt;x+1&gt;::value

那么这个场景的一般策略是什么?

【问题讨论】:

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


    【解决方案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';
    }
    

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-11-10
          • 2021-01-16
          • 1970-01-01
          • 1970-01-01
          • 2012-08-29
          • 1970-01-01
          • 2021-12-22
          • 1970-01-01
          相关资源
          最近更新 更多