Search code examples
c++arraysc++11templates

Concatenating a sequence of std::arrays


Consider the following: (Wandbox)

#include <array>
#include <algorithm>
#include <iostream>

template<typename T, int N, int M>
auto concat(const std::array<T, N>& ar1, const std::array<T, M>& ar2)
{
    std::array<T, N+M> result;
    std::copy (ar1.cbegin(), ar1.cend(), result.begin());
    std::copy (ar2.cbegin(), ar2.cend(), result.begin() + N);
    return result;
}

int main()
{
    std::array<int, 3> ar1 = {1, 2, 3};
    std::array<int, 2> ar2 = {4, 5};
    auto result = concat<int, 3, 2>(ar1, ar2);
    for (auto& x : result)
        std::cout << x << " ";
    std::cout << std::endl;
    return 0;
}

Given a sequence of std::array<T, length1>, std::array<T, length2>, ..., std::array<T, lengthK>, how can I generalize the above code and write a function which concatenates the sequence into an std::array<T, sum(lengths)>?

It would be nice if there is a way to write a reusable function which reduces a similar sequence of template classes using a given binary operation, e.g., use concat in the example above, rather than writing a special method (which would have to be re-written each time the binary op changes).

(IIUC, the relevant Standard Library algorithms (accumulate, reduce) only work in case the class of the result of the binary operation is always the same.)


Solution

  • You may do the following:

    template <typename F, typename T, typename T2>
    auto func(F f, T&& t, T2&& t2)
    {
        return f(std::forward<T>(t), std::forward<T2>(t2));
    }
    
    template <typename F, typename T, typename T2, typename ... Ts>
    auto func(F f, T&& t, T2&& t2, Ts&&...args)
    {
        return func(f, f(std::forward<T>(t), std::forward<T2>(t2)), std::forward<Ts>(args)...);
    }
    

    With usage

    struct concatenater
    {
        template<typename T, std::size_t N, std::size_t M>
        auto operator()(const std::array<T, N>& ar1, const std::array<T, M>& ar2) const
        {
            std::array<T, N+M> result;
            std::copy (ar1.cbegin(), ar1.cend(), result.begin());
            std::copy (ar2.cbegin(), ar2.cend(), result.begin() + N);
            return result;
        }
    };
    

    and

    auto result = func(concatenater{}, ar1, ar2, ar3, ar4);
    

    C++14 Demo
    C++11 Demo