25

I have to following problem:

template< size_t... N_i >
class A
{
  // ...
};

template< size_t N, size_t... N_i >
A</* first N elements of N_i...*/> foo()
{
  A</* first N elements of N_i...*/> a;

  // ...

  return a;
}

int main()
{
  A<1,2> res = foo<2, 1,2,3,4>();

  return 0;
}

Here, I want foo to have the return type A</* first N size_t of N_i...*/>, i.e., the class A which has as template arguments the first N elements of the parameter pack N_i.

Does anyone know how this can be implemented?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
abraham_hilbert
  • 2,221
  • 1
  • 13
  • 30

6 Answers6

17

Here is the shortest solution that came to my mind (with two lines spent for an alias).
It follows a minimal, working example based on the code posted by the OP:

#include<functional>
#include<cstddef>
#include<utility>
#include<tuple>

template<std::size_t... V>
class A {};

template<std::size_t... V, std::size_t... I>
constexpr auto func(std::index_sequence<I...>) {
    return A<std::get<I>(std::make_tuple(V...))...>{};
}

template<std::size_t N, std::size_t... V>
constexpr auto func() {
    return func<V...>(std::make_index_sequence<N>{});
}

template<std::size_t N, std::size_t... V>
using my_a = decltype(func<N, V...>());

int main() {
    A<1,2> res1 = func<2, 1, 2, 3, 4>();
    // Or even better...
    decltype(func<2, 1, 2, 3, 4>()) res2{};
    // Or even better...
    my_a<2, 1, 2, 3, 4> res3{};
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
10

This is a slight variation on @skypjack's answer that avoids using tuples:

template <size_t... N_i,size_t... M_i>
auto foo2(std::index_sequence<M_i...>)
{
    constexpr size_t values[] = {N_i...};
    return A<values[M_i]...>();
}

template <size_t N,size_t... N_i>
auto foo()
{
    return foo2<N_i...>(std::make_index_sequence<N>());
}
Community
  • 1
  • 1
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • 3
    You missed something in your _slight variation_. As it is it cannot be used anymore in a constant expression. You should add a `constexpr` in front of the functions. If you decide to copy an answer, do it to the end. ;-) – skypjack Oct 23 '16 at 07:46
9

The most direct subproblem is in the land of typelists:

template <class... Ts>
struct typelist {
    using type = typelist;
    static constexpr std::size_t size = sizeof...(Ts);
};

template <class T>
struct tag { using type = T; };

template <std::size_t N, class TL>
struct head_n {
    using type = ???;
};

Now, head_n is just a matter of simple recursion - move an element from one list to another list N times starting from an empty list.

template <std::size_t N, class R, class TL>
struct head_n_impl;

// have at least one to pop from and need at least one more, so just 
// move it over
template <std::size_t N, class... Ts, class U, class... Us>
struct head_n_impl<N, typelist<Ts...>, typelist<U, Us...>>
: head_n_impl<N-1, typelist<Ts..., U>, typelist<Us...>>
{ };

// we have two base cases for 0 because we need to be more specialized
// than the previous case regardless of if we have any elements in the list
// left or not
template <class... Ts, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<Us...>>
: tag<typelist<Ts...>>
{ };

template <class... Ts, class U, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<U, Us...>>
: tag<typelist<Ts...>>
{ };

template <std::size_t N, class TL>
using head_n = typename head_n_impl<N, typelist<>, TL>::type;

Going from this to your specific problem I leave as an exercise to the reader.


An alternate approach is via concatenation. Convert every element of a typelist<Ts...> into either a typelist<T> or a typelist<>, and then concat them all together. concat is straightforward:

template <class... Ts>
struct concat { };

template <class TL>
struct concat<TL>
: tag<TL>
{ };

template <class... As, class... Bs, class... Rest>
struct concat<typelist<As...>, typelist<Bs...>, Rest...>
: concat<typelist<As..., Bs...>, Rest...>
{ };

And then we can do:

template <std::size_t N, class TL, class = std::make_index_sequence<TL::size>>
struct head_n;

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
: concat<
        std::conditional_t<(Is < N), typelist<Ts>, typelist<>>...
        >
{ };

template <std::size_t N, class TL>
using head_n_t = typename head_n<N, TL>::type;

The advantage of this latter approach is that concat can be replaced in C++17 by a fold-expression given an appropriate operator+:

template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
    return {};
}

which allows:

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
{
    using type = decltype(
        (std::conditional_t<(Is < N), typelist<Ts>, typelist<>>{} + ... + typelist<>{})
        );
};        
Barry
  • 286,269
  • 29
  • 621
  • 977
4

This is fairly simple with Boost.Hana:

namespace hana = boost::hana;

template<size_t... vals>
auto make_a(hana::tuple<hana::integral_constant<size_t, vals>...>)
{
    return A<vals...>{};
}

template<size_t N, size_t... vals>
auto foo(){
    constexpr auto front = hana::take_front(
        hana::tuple_c<size_t, vals...>,
        hana::integral_c<size_t,N>
    );
    return detail::make_a(front);
}

live demo

krzaq
  • 16,240
  • 4
  • 46
  • 61
3

You could also make use of variadic generic lambda expression and reusable helper structure to perform compile-time iteration:

#include <utility>
#include <tuple>

template <std::size_t N, class = std::make_index_sequence<N>>
struct iterate;

template <std::size_t N, std::size_t... Is>
struct iterate<N, std::index_sequence<Is...>> {
   template <class Lambda>
   auto operator()(Lambda lambda) {
      return lambda(std::integral_constant<std::size_t, Is>{}...);
   }
};

template <size_t... Is>
struct A { };

template <size_t N, size_t... Is>
auto foo() {
   return iterate<N>{}([](auto... ps){
      using type = std::tuple<std::integral_constant<std::size_t, Is>...>;
      return A<std::tuple_element_t<ps, type>{}...>{};
   });
}

int main() {
   decltype(foo<3, 1, 2, 3, 4>()) a; // == A<1, 2, 3> a;
}
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • @skypjack by the way I actually think this solution isn't tricky at all and may be quite extensible :) – W.F. Oct 18 '16 at 21:04
  • Do not misunderstand, I like the way you combined defaulted parameter, partial specialization and lambda function. I only guess it's too tricky for the specific case. I'm of the party for which if you can do it in a line, you should not waste the one below. That's all. Nice solution anyway. ;-) – skypjack Oct 18 '16 at 21:07
  • @skypjack Well I guess its a good party ;) Yet I might also be misunderstood... Once struct `iterate` is created you could use it whenever compile-time iteration over `N` elements is needed. This would make other places where you previously needed to create two functions -- the one with `std::make_index_sequence` and its `impl` version with `std::index_sequence` unnecessary. Do you get my point now? :) – W.F. Oct 18 '16 at 21:15
  • 1
    I got it two or three comments ago. :-D ... I see what you mean, but still I guess you can write it in a more compact form. Anyway, I like this for it's a brain-gym one. ;-) – skypjack Oct 18 '16 at 21:19
  • 1
    @skypjack hah brain-gym I gotta remember that :) Well you confused me with the `I'm of the party for which if you can do it in a line, you should not waste the one below` comment :) To be honest I also think your solution could actually be the best for OP, but maybe someone else in the future might find the code better in his or hers particular problem :) – W.F. Oct 18 '16 at 21:29
  • 2
    You can reduce `decltype(ps)::value` to just `ps`. And the whole `tuple_element` stuff to `A{}...>{}` – Barry Oct 19 '16 at 21:25
2

Unfortunately, such method requires to define additional Helper types

template< size_t... N_i >
class A
{
};

template <size_t... N_i>
struct Helper;

template <size_t... N_i>
struct Helper<0, N_i...>
{
    typedef A<> type;
};

template <size_t N0, size_t... N_i>
struct Helper<1, N0, N_i...>
{
    typedef A<N0> type;
};

template <size_t N0, size_t N1, size_t... N_i>
struct Helper<2, N0, N1, N_i...>
{
    typedef A<N0, N1> type;
};

template< size_t N, size_t... N_i >
typename Helper<N, N_i...>::type foo()
{
  typename Helper<N, N_i...>::type a;
  return a;
}
Alex Aparin
  • 4,393
  • 5
  • 25
  • 51