13

Since I was not able to find such a function (incorrectly?), I'm trying to make a compile-time function (constexpr) function which takes a std::array<T,n> arr and a T t and returns a new std::array<T,n+1> with t added to the end of arr. I've started with something like this:

template <typename T, int n>
constexpr std::array<T,n+1> append(std::array<T,n> a, T t);

template <typename T>
constexpr std::array<T,1> append(std::array<T,0> a, T t)
{
  return std::array<T,1>{t};
}

template <typename T>
constexpr std::array<T,2> append(std::array<T,1> a, T t)
{
  return std::array<T,2>{a[0], t};
}

Here I get stuck. What I need is a way to expand a in the first n places of the initializer list, and then add t add the end. Is that possible? Or is there another way of doing this?

kalj
  • 1,432
  • 2
  • 13
  • 30

2 Answers2

21

Of course, it is possible: std::index_sequence<I...> is your friend! You'd simply dispatch to a function which takes a suitable std::index_sequence<I...> as argument and expands the pack with all the values. For example:

template <typename T, std::size_t N, std::size_t... I>
constexpr std::array<T, N + 1>
append_aux(std::array<T, N> a, T t, std::index_sequence<I...>) {
    return std::array<T, N + 1>{ a[I]..., t };
}
template <typename T, std::size_t N>
constexpr std::array<T, N + 1> append(std::array<T, N> a, T t) {
    return append_aux(a, t, std::make_index_sequence<N>());
}
Edward Z. Yang
  • 26,325
  • 16
  • 80
  • 110
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Wow, pure magic to me! C++ sure has come a long way in recent year. Thanks! – kalj Dec 30 '16 at 15:53
  • It is worth adding that for c++11 there are available efficient implementations of `integer_sequence` e.g. [this one](https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h) as it unfortunately does not come out of the box. – W.F. Dec 30 '16 at 15:55
  • 1
    @kalj: actually, there isn't much magic. Maybe it is natural to me because I posted [this answer](http://stackoverflow.com/a/14059084) which led to the relevant [proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html). ;-) – Dietmar Kühl Dec 30 '16 at 15:57
  • 1
    @DietmarKühl, well if this was magic that answer is pure witchcraft to me. So much cool stuff to learn though! – kalj Dec 30 '16 at 16:08
  • Okay, it works with some slight modifications. Turns out `N` must be `std::size_t`, and there is also a small typo in that append should return an array of size `N+1`. You might want to update this. – kalj Dec 30 '16 at 16:18
  • @kalj: I had typed the code in without testing - I should have mentioned that (your question wasn't a complete program and I didn't feel like typing in the scaffolding). I think I fixed the typos you noticed. – Dietmar Kühl Dec 30 '16 at 16:21
  • Note: requires C++14 -- http://en.cppreference.com/w/cpp/utility/integer_sequence – Brent Bradburn Apr 18 '17 at 20:42
1

It was easy to extend Dietmar's answer to a util that lets you constexpr- catenate two arrays:

// constexpr util to catenate two array's.
//
// Usage:
//
// constexpr std::array<int, 2> a1 = { 1, 2 };
// constexpr std::array<int, 2> a2 = { 3, 4 };
//
// constexpr auto a3 = catenate_array(a1, a2);

template <typename T, std::size_t N, std::size_t M, std::size_t... I, std::size_t... J>
constexpr std::array<T, N + M>
catenate_array_aux(std::array<T, N> a1, std::array<T, M> a2, std::index_sequence<I...>, std::index_sequence<J...>) {
    return std::array<T, N + M>{ a1[I]..., a2[J]... };
}

template <typename T, std::size_t N, std::size_t M>
constexpr std::array<T, N + M> catenate_array(std::array<T, N> a1, std::array<T, M> a2) {
    return catenate_array_aux(a1, a2, std::make_index_sequence<N>(), std::make_index_sequence<M>());
}
Carlo Wood
  • 5,648
  • 2
  • 35
  • 47