Frage

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?

War es hilfreich?

Lösung

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:

  • baseline declaration
  • specialization (to end recursion or limit to "interesting" types)
  • alias declaration

on the other hand it's only written once.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top