Reputation: 7881
So someone asked a question then deleted it, but I found it an interesting challenge.
They had this weird type, and had run into the problem that Concat and Range were castable to Sequences but were not Sequences (for obvious reasons)
template<unsigned... Is> struct Sequence{};
template<typename... Is> struct Concat{};
template<unsigned... Is> struct Concat<Sequence<Is...>> : public Sequence<Is...> {};
template<unsigned... Is, unsigned... Js, typename... Rest>
struct Concat<Sequence<Is...>, Sequence<Js...>, Rest...>
: Concat<Sequence<Is..., Js...>, Rest...> {};
So I figured it'd be an interesting challenge to do this right, making everything the same type.
So my first pass at Concat ran into unexpected problems...How on earth does one extract the template parameters for the Sequences? I ended up using a function below:
template <unsigned... Is,unsigned... Js>
constexpr auto concat_results(Sequence<Is...>,Sequence<Js...>) -> Sequence<Is...,Js...>
{
return Sequence<Is...,Js...>();
}
template <typename seq1,typename seq2>
using Concat = decltype(concat_results(seq1(),seq2()));
Bizarrely, I think that's the shortest way to declare it, though it is a little weird. However, the concat_results could be useful (on references of the sequences) and the type can be useful in other places. Anyone have a better way to do that?
Next up was range, which I did with a recursive struct helper:
template <unsigned N>
struct nrange_helper
{
using Range = Concat<typename nrange_helper<N-1>::Range,Sequence<N>>;
};
template <>
struct nrange_helper<0>
{
using Range = Sequence<0>;
};
template <unsigned N>
using Range = typename nrange_helper<N>::Range;
Again, its weird, but it works...but I feel like there should be a better way. Anyone got one?
Upvotes: 3
Views: 194
Reputation: 299999
Note: please check out std::integer_sequence
, its specialization index_sequence
and its make_index_sequence
and index_sequence_for
facilities.
If you want to declare them yourself, you generally would use inner types:
template <typename, typename> struct Concat;
template <unsigned... Is, unsigned... Js>
struct Concat<Sequence<Is...>,Sequence<Js...>> {
using type = Sequence<Is..., Js...>;
};
template <unsigned N>
struct Range {
using type = typename Concat<typename Range<N-1>::type, Sequence<N>>::type;
};
template <> struct Range<0> { using type = Sequence<0>; };
Which can of course be combined to template aliases for a shorter final form:
template <typename, typename> struct ConcatH;
template <unsigned... Is, unsigned... Js>
struct ConcatH<Sequence<Is...>,Sequence<Js...>> {
using type = Sequence<Is..., Js...>;
};
template <typename L, typename R>
using Concat = typename ConcatH<L, R>::type;
and:
template <unsigned N>
struct RangeH {
using type = Concat<typename RangeH<N-1>::type, Sequence<N>>;
};
template <> struct RangeH<0> { using type = Sequence<0>; };
template <unsigned N>
using Range = typename RangeH<N>::type;
However, indeed, this is slightly verbose. Still, there is little cruft here:
on the other hand it's only written once.
Upvotes: 1