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, ~);
}
8GBのRAMを搭載したクアッドコアi5システムで、GCC 4.8.0でプログラムをコンパイルしました。コンパイルの成功には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で正常にコンパイルしました。
ただし、この2番目のテスト:
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_type
_とsize()
を追加できないと感じるまで変更しました(ただし_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のソリューションの「魔法」は、正確に2つのパラメーターを持つ__merge_and_renumber
_(彼のコードではconcat
)の宣言にありますが、仕様化は個々のパラメーターパックを効果的に公開します。
typename
..._::type
_ in ...
_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
サイズの構造が許可され、他の何かが最初に壊れるため、対数の深さを引くだけで十分です。 (公平を期すために、2^10
サイズの構造の90の異なる層が発生する可能性が高くなります)。
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>;
デモ: coliru
シンプルな実装O(N)。おそらく、大きなNに必要なものではないかもしれませんが、私のアプリケーションはインデックス付き引数を使用して関数を呼び出すためだけのものであり、約10を超えるアリティは期待していません。標準ライブラリの実装を使用し、これを否定することを楽しみにしています:)
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;