Implementation C++14 make_integer_sequence

Implementation C++14 make_integer_sequence

本文关键字:integer sequence make C++14 Implementation      更新时间:2023-10-16

我尝试实现 C++14 别名模板make_integer_sequence,这简化了类模板integer_sequence的创建。

template< class T, T... I> struct integer_sequence
{
    typedef T value_type;
    static constexpr size_t size() noexcept { return sizeof...(I) ; }
};
template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.

要实现make_integer_sequence我们需要一个帮助程序结构make_helper

template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;

实施make_helper并不太困难。

template< class T, T N, T... I >
struct make_helper
{
   typedef typename mpl::if_< T(0) == N,  
                  mpl::identity< integer_sequence<T,I...> >,
                  make_helper< T, N-1, N-1,I...> 
               >::type;
};

为了测试make_integer_sequence我做了这个主要功能:

int main()
{
    #define GEN(z,n,temp)   
     typedef make_integer_sequence< int, n >  BOOST_PP_CAT(int_seq,n) ;
   BOOST_PP_REPEAT(256, GEN, ~);
}

我用GCC 4.8.0编译了该程序,在具有8GB RAM的四核i5系统上。成功编译需要 4 秒。

但是,当我将 GEN 宏更改为:

int main() {
#define GEN(z,n,temp) 
typedef make_integer_sequence< int, n * 4 > BOOST_PP_CAT(int_seq, n) ;
BOOST_PP_REPEAT(256, GEN, ~ );
}

编译失败并输出错误消息:

虚拟内存已耗尽。

有人可以解释这个错误以及导致它的原因吗?

编辑:

我将测试简化为:

int main()
{
   typedef make_integer_sequence< int, 4096 > int_seq4096;
}

然后我成功地用GCC 4.8.0 -ftemplate-depth=65536编译。

然而,这第二个测试:

int main()
{
    typedef make_integer_sequence< int, 16384 > int_seq16384;
}

未使用 GCC 4.8.0 -ftemplate-depth=65536 进行编译,并导致错误:

虚拟内存已耗尽。

所以,我的问题是,如何减少模板深度实例化?

问候库尔希德。

这是一个log N实现,它甚至不需要增加模板实例化的最大深度,并且编译速度非常快:

// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<unsigned...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
  : seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template<unsigned N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};

这基本上是我对Xeo的解决方案的黑客攻击:制作社区wiki - 如果赞赏,请对Xeo投赞成票

。只是修改,直到我觉得它不能变得更简单,重命名并根据标准添加value_typesize()(但只做index_sequence而不是integer_sequence(,使用 GCC 5.2 的代码-std=c++14可以在我坚持的旧/其他编译器下运行。 可能会为某人节省一些时间/混乱。

// based on http://stackoverflow.com/a/17426611/410767 by Xeo
namespace std  // WARNING: at own risk, otherwise use own namespace
{
    template <size_t... Ints>
    struct index_sequence
    {
        using type = index_sequence;
        using value_type = size_t;
        static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
    };
    // --------------------------------------------------------------
    template <class Sequence1, class Sequence2>
    struct _merge_and_renumber;
    template <size_t... I1, size_t... I2>
    struct _merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
      : index_sequence<I1..., (sizeof...(I1)+I2)...>
    { };
    // --------------------------------------------------------------
    template <size_t N>
    struct make_index_sequence
      : _merge_and_renumber<typename make_index_sequence<N/2>::type,
                            typename make_index_sequence<N - N/2>::type>
    { };
    template<> struct make_index_sequence<0> : index_sequence<> { };
    template<> struct make_index_sequence<1> : index_sequence<0> { };
}

笔记:

  • Xeo解决方案的"魔力"在于_merge_and_renumber声明(concat在他的代码中(,正好有两个参数,而规范有效地公开了它们各自的参数包

  • typename...::type...

    struct make_index_sequence
      : _merge_and_renumber<typename make_index_sequence<N/2>::type,
                            typename make_index_sequence<N - N/2>::type>
    

    避免以下错误:

invalid use of incomplete type 'struct std::_merge_and_renumber<std::make_index_sequence<1ul>, std::index_sequence<0ul> >'

我发现了make_index_sequence实现的非常快速且不必要的深度递归版本。在我的 PC 中,它的编译速度为 N = 1 048 576 ,用 2 秒。(PC : Centos 6.4 x86, i5, 8 Gb RAM, gcc-4.4.7 -std=c++0x -O2 -Wall(。

#include <cstddef> // for std::size_t
template< std::size_t ... i >
struct index_sequence
{
    typedef std::size_t value_type;
    typedef index_sequence<i...> type;
    // gcc-4.4.7 doesn't support `constexpr` and `noexcept`.
    static /*constexpr*/ std::size_t size() /*noexcept*/
    { 
        return sizeof ... (i); 
    }
};

// this structure doubles index_sequence elements.
// s- is number of template arguments in IS.
template< std::size_t s, typename IS >
struct doubled_index_sequence;
template< std::size_t s, std::size_t ... i >
struct doubled_index_sequence< s, index_sequence<i... > >
{
    typedef index_sequence<i..., (s + i)... > type;
};
// this structure incremented by one index_sequence, iff NEED-is true, 
// otherwise returns IS
template< bool NEED, typename IS >
struct inc_index_sequence;
template< typename IS >
struct inc_index_sequence<false,IS>{ typedef IS type; };
template< std::size_t ... i >
struct inc_index_sequence< true, index_sequence<i...> >
{
    typedef index_sequence<i..., sizeof...(i)> type;
};

// helper structure for make_index_sequence.
template< std::size_t N >
struct make_index_sequence_impl : 
           inc_index_sequence< (N % 2 != 0), 
                typename doubled_index_sequence< N / 2,
                           typename make_index_sequence_impl< N / 2> ::type
               >::type
       >
{};
 // helper structure needs specialization only with 0 element.
template<>struct make_index_sequence_impl<0>{ typedef index_sequence<> type; };

// OUR make_index_sequence,  gcc-4.4.7 doesn't support `using`, 
// so we use struct instead of it.
template< std::size_t N >
struct make_index_sequence : make_index_sequence_impl<N>::type {};
//index_sequence_for  any variadic templates
template< typename ... T >
struct index_sequence_for : make_index_sequence< sizeof...(T) >{};

// test
typedef make_index_sequence< 1024 * 1024 >::type a_big_index_sequence;
int main(){}

您在这里缺少一个-1

typedef typename mpl::if_< T(0) == N,  
              mpl::identity< integer_sequence<T> >,
              make_helper< T, N, N-1,I...> 
           >::type;

特别:

typedef typename mpl::if_< T(0) == N,  
              mpl::identity< integer_sequence<T> >,
              make_helper< T, N-1, N-1,I...> 
           >::type;

接下来,第一个分支不应该是integer_sequence<T>,而是integer_sequence<T, I...>

typedef typename mpl::if_< T(0) == N,  
              mpl::identity< integer_sequence<T, I...> >,
              make_helper< T, N-1, N-1,I...> 
           >::type;

这应该足以让你的原始代码编译。

一般来说,在编写严肃的元编程template时,你的主要目标应该是降低template实例化的深度。 思考这个问题的一种方法是想象你有一台无限线程计算机:每个独立的计算都应该被洗牌到它自己的线程上,然后在最后洗牌在一起。 你有一些需要 O(1( 深度的操作,比如...扩展:利用它们。

通常,拉动对数深度就足够了,因为深度900,这允许2^900大小的结构,而其他东西首先会破坏。 (公平地说,更有可能发生的是90层不同的2^10大小的结构(。

这是 Xeo 对数答案的另一个稍微更一般的变体,它为任意类型提供了make_integer_sequence。这是通过使用std::integral_constant来完成的,以避免可怕的"模板参数涉及模板参数"错误。

template<typename Int, Int... Ints>
struct integer_sequence
{
    using value_type = Int;
    static constexpr std::size_t size() noexcept
    {
        return sizeof...(Ints);
    }
};
template<std::size_t... Indices>
using index_sequence = integer_sequence<std::size_t, Indices...>;
namespace
{
    // Merge two integer sequences, adding an offset to the right-hand side.
    template<typename Offset, typename Lhs, typename Rhs>
    struct merge;
    template<typename Int, Int Offset, Int... Lhs, Int... Rhs>
    struct merge<
        std::integral_constant<Int, Offset>,
        integer_sequence<Int, Lhs...>,
        integer_sequence<Int, Rhs...>
    >
    {
        using type = integer_sequence<Int, Lhs..., (Offset + Rhs)...>;
    };
    template<typename Int, typename N>
    struct log_make_sequence
    {
        using L = std::integral_constant<Int, N::value / 2>;
        using R = std::integral_constant<Int, N::value - L::value>;
        using type = typename merge<
            L,
            typename log_make_sequence<Int, L>::type,
            typename log_make_sequence<Int, R>::type
        >::type;
    };
    // An empty sequence.
    template<typename Int>
    struct log_make_sequence<Int, std::integral_constant<Int, 0>>
    {
        using type = integer_sequence<Int>;
    };
    // A single-element sequence.
    template<typename Int>
    struct log_make_sequence<Int, std::integral_constant<Int, 1>>
    {
        using type = integer_sequence<Int, 0>;
    };
}
template<typename Int, Int N>
using make_integer_sequence =
    typename log_make_sequence<
        Int, std::integral_constant<Int, N>
    >::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;

演示:科里鲁

简单实现 O(N(。可能不是你想要的大 N,但我的应用程序仅用于调用带有索引参数的函数,我不希望超过 10 个。我还没有填写integer_sequence的成员。我期待着使用标准库实现并取消此:)

template <typename T, T... ints>
struct integer_sequence
{ };
template <typename T, T N, typename = void>
struct make_integer_sequence_impl
{
    template <typename>
    struct tmp;
    template <T... Prev>
    struct tmp<integer_sequence<T, Prev...>>
    {
        using type = integer_sequence<T, Prev..., N-1>;
    };
    using type = typename tmp<typename make_integer_sequence_impl<T, N-1>::type>::type;
};
template <typename T, T N>
struct make_integer_sequence_impl<T, N, typename std::enable_if<N==0>::type>
{ using type = integer_sequence<T>; };
template <typename T, T N>
using make_integer_sequence = typename make_integer_sequence_impl<T, N>::type;

这是另一种实现技术(对于T=size_t(,它使用 C++17 折表达式和按位生成(即 O(log(N) (:

template <size_t... Is>
struct idx_seq {
  template <size_t N, size_t Offset>
  struct pow2_impl {
    using type = typename idx_seq<Is..., (Offset + Is)...>::template pow2_impl<N - 1, Offset + sizeof...(Is)>::type;
  };
  template <size_t _> struct pow2_impl<0, _> { using type = idx_seq; };
  template <size_t _> struct pow2_impl<(size_t)-1, _> { using type = idx_seq<>; };
  template <size_t Offset>
  using offset = idx_seq<(Offset + Is)...>;
};
template <size_t N>
using idx_seq_pow2 = typename idx_seq<0>::template pow2_impl<N, 1>::type;
template <size_t... Is, size_t... Js>
constexpr static auto operator,(idx_seq<Is...>, idx_seq<Js...>)
  -> idx_seq<Is..., Js...>
{ return {}; }
template <size_t N, size_t Mask, size_t... Bits>
struct make_idx_seq_impl {
  using type = typename make_idx_seq_impl<N, (N >= Mask ? Mask << 1 : 0), Bits..., (N & Mask)>::type;
};
template <size_t N, size_t... Bits>
struct make_idx_seq_impl<N, 0, Bits...> {
  using type = decltype((idx_seq<>{}, ..., typename idx_seq_pow2<Bits>::template offset<(N & (Bits - 1))>{}));
};
template <size_t N>
using make_idx_seq = typename make_idx_seq_impl<N, 1>::type;

这是一个非常简单的解决方案,基于标签调度的递归实现

template <typename T, T M, T ... Indx>
constexpr std::integer_sequence<T, Indx...> make_index_sequence_(std::false_type)
{
    return {};
}
template <typename T, T M, T ... Indx>
constexpr auto make_index_sequence_(std::true_type)
{
    return make_index_sequence_<T, M, Indx..., sizeof...(Indx)>(
            std::integral_constant<bool, sizeof...(Indx) + 1 < M>());
}
template <size_t M>
constexpr auto make_index_sequence()
{
    return make_index_sequence_<size_t, M>(std::integral_constant<bool, (0 < M)>());
}

但是,此解决方案不能扩展到 C++11。