6

Given two constexpr arrays (type[N] or std::array<type, N>)

constexpr int A[5] { 0, 1, 2, 3, 4 };
constexpr int B[5] { 5, 4, 3, 2, 1 };

is it possible to initialize a new constexpr array performing an element-wise operation (or constexpr function)?

For example, can this code

constexpr int sum(int i) { return A[i] + B[i]; }
constexpr int S[5] { sum(0), sum(1), sum(2), sum(3), sum(4) };

be rewritten more conveniently in a form that calls sum(i) for every element in S?

1

1 Answer 1

10

Similar things have been done many times already, but here's a solution for this particular compile-time operation on arrays ;)

template<int... Is>
struct seq {};
template<int I, int... Is>
struct gen_seq : gen_seq<I-1, I-1, Is...> {};
template<int... Is>
struct gen_seq<0, Is...> : seq<Is...> {};

#include <array>

template<class T, int N, int... Is>
constexpr std::array<T, N> sum(T const (&lhs)[N], T const (&rhs)[N], seq<Is...>)
{
    return {{lhs[Is]+rhs[Is]...}};
}

template<class T, int N>
constexpr auto sum(T const (&lhs)[N], T const (&rhs)[N])
-> decltype( sum(lhs, rhs, gen_seq<N>{}) )
{
    return sum(lhs, rhs, gen_seq<N>{});
}

#include <iostream>
int main()
{
    constexpr int a[] = {1,2,3,4,5};
    constexpr int b[] = {1,2,3,4,5};
    constexpr auto c = sum(a,b);

    for(auto e : c) std::cout << e << ", ";
}

N.B. std::array::operator[] is not constexpr in C++11, therefore I've used raw arrays as input.


For arbitrary binary functions:

template<class T, int N, class F, int... Is>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f,
                         seq<Is...>)
-> std::array<decltype( f(lhs[0], rhs[0]) ), N>
{
    return {{ f(lhs[Is], rhs[Is])... }};
}

template<class T, int N, class F>
constexpr auto transform(T const (&lhs)[N], T const (&rhs)[N], F f)
-> decltype( transform(lhs, rhs, f, gen_seq<N>{}) )
{
    return transform(lhs, rhs, f, gen_seq<N>{});
}

constexpr int sum(int l, int r) { return l+r; }
// ...
constexpr auto c = transform(a,b,sum);

For arbitrary n-ary functions and arbitrary array-like types:

template<class F, class... Args>
constexpr auto index_invoke(F f, int i, Args&&... args)
-> decltype( f(args[i]...) )
{
    return f(args[i]...);
}

template<class F, int... Is, class... Args>
constexpr auto transform_impl(F f, seq<Is...>, Args&&... args)
-> std::array<decltype( f(args[0]...) ), sizeof...(Is)>
{
    return {{ index_invoke(f, Is, std::forward<Args>(args)...)... }};
}

template <class T, class...>
struct get_extent_helper
: std::integral_constant<int,
                         std::extent<typename std::remove_reference<T>::type>{}>
{};

template<class F, class... Args>
constexpr auto transform(F f, Args&&... args)
-> decltype( transform_impl(f, gen_seq< get_extent_helper<Args...>{} >{},
                            std::forward<Args>(args)...) )
{
    using N = get_extent_helper<Args...>;
    return transform_impl(f, gen_seq<N{}>{}, std::forward<Args>(args)...);
}

More lightweight with an alias-template:

template <class T, class...>
using get_extent_helper =
  std::integral_constant<int,
                         std::extent<typename std::remove_reference<T>::type>{}>;

But this is buggy under g++4.8.1

Sign up to request clarification or add additional context in comments.

4 Comments

Awesome! I learned a bunch from this answer, thanks very much!
nitpick: std::get is constexpr for std::arrays
@TomKnapen "The overloads are marked as constexpr since C++14." :(
For anyone worried about the performance of the above, I did extensive tests of compiling his transform "arbitrary binary" using lambda's for F vs a simple loop and in all cases, using optimized settings, the code created on x86 was similar if not exactly the same. In large arrays the compiler made a looping function and short ones it just unrolled both.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.