0

I need a function fulfilling the following functionality:

template<typename T, std::size_t SIZE_L, std::size_t ...SIZE_R>
std::array<T, SIZE_L> concat(const std::array<T, SIZE_R...>&);

This is supposed to concatenate all passed arrays on the RHS into a single array. The sum of SIZE_R should be identical to SIZE_L -- and if possible one should not need to specify SIZE_L by hand.

The element type of this array will be always double.

5
  • 1
    can you use auto as return type? Commented Feb 22, 2021 at 13:40
  • 2
    SIZE_L -> (... + SIZE_R) (C++17) Commented Feb 22, 2021 at 13:40
  • Is T default constructible? Commented Feb 22, 2021 at 13:42
  • 1
    But you can do: std::array<NonDefaultConstructible, 2> arr{{ NonDefaultConstructible(42), NonDefaultConstructible(51) }};... Commented Feb 22, 2021 at 13:50
  • @ Jarod42 -- sorry -- it is default constructible! Commented Feb 22, 2021 at 14:00

2 Answers 2

4

With default constructible T, you might do (C++17):

template<typename T, std::size_t ...Ns>
std::array<T, (... + Ns)> concat(const std::array<T, Ns>&... arrs)
{
    std::array<T, (... + Ns)> res;
    std::size_t offset = 0;
    
    ((std::copy(arrs.begin(), arrs.end(), res.begin() + offset),
      offset += arrs.size()),
     ...);
    return res;
}

Demo

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

2 Comments

@FrankPuck: Concatenation would require some copies... (My version does extra default construction though).
range-v3 has a concat view, but then, result would no longer be contiguous.
3

If needed, here's a version that can deal with a type which can't be default-constructed (try it online):

#include <array>
#include <type_traits>
#include <utility>
#include <tuple>

namespace array_concat_helper {

template <std::size_t Idx, typename SizeSeq, typename Enable=void>
struct indices
{
    static constexpr std::size_t tuple_index = 0;
    static constexpr std::size_t elem_index = Idx;
};

template <std::size_t Idx, std::size_t FirstN, std::size_t ...Ns>
struct indices<Idx, std::index_sequence<FirstN, Ns...>,
    std::enable_if_t<(Idx >= FirstN)>>
{
    static constexpr std::size_t tuple_index =
    1 + indices<Idx-FirstN, std::index_sequence<Ns...>>::tuple_index;
    static constexpr std::size_t elem_index =
    indices<Idx-FirstN, std::index_sequence<Ns...>>::elem_index;
};

template <typename T, std::size_t ...Ns, std::size_t ...Is>
std::array<T, (... + Ns)> concat(
    std::index_sequence<Is...>,
    const std::array<T, Ns>&... arrs)
{
    auto arr_tuple = std::tie(arrs...);
    return {{
        std::get<indices<Is, std::index_sequence<Ns...>>::tuple_index>
        (arr_tuple)
        [indices<Is, std::index_sequence<Ns...>>::elem_index]...
    }};
}

} // end namespace array_concat_helper

template<typename T, std::size_t ...Ns>
std::array<T, (... + Ns)> concat(const std::array<T, Ns>&... arrs)
{
    return array_concat_helper::concat(
        std::make_index_sequence<(... + Ns)>{}, arrs...);
}

3 Comments

Nice, can it make coffee and use movable sources too?
@Surt Not this version. But hmm, if we took the arrays as forwarding references and just used typename C::value_type and std::tuple_size, then we could mix and match lvalue and rvalue arrays with perfect forwarding...
Sad of the complexity induced by handling no default constructible types :-/

Your Answer

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