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?

5
  • Lots of minging recursion Commented Oct 18, 2016 at 15:48
  • Thank you. Is there no "elegant" solution? Commented Oct 18, 2016 at 15:50
  • 29
    @abraham_hilbert: You're doing C++ template metaprogramming, and you want an "elegant" solution? 😂 Commented Oct 18, 2016 at 15:59
  • 3
    @LightnessRacesinOrbit sometimes it's not impossible ;) Commented Oct 18, 2016 at 15:59
  • 2
    @krzaq Probably also this time it's not impossible. ;-) Commented Oct 18, 2016 at 18:08

6 Answers 6

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{}; } 
Sign up to request clarification or add additional context in comments.

2 Comments

That's a great use of constexprness of make_tuple and get.
@krzaq Thank you. Hope this helps the OP.
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>()); } 

1 Comment

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. ;-)
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<>{}) ); }; 

Comments

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

Comments

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; } 

6 Comments

@skypjack by the way I actually think this solution isn't tricky at all and may be quite extensible :)
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. ;-)
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 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 :)
You can reduce decltype(ps)::value to just ps. And the whole tuple_element stuff to A<std::tuple_element_t<ps, type>{}...>{}
|
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; } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.