2

How can I construct an std::array out of several smaller ones, plus not-array elements?

I.e.:

std::array<std::string, 2> strings = { "a", "b" };
std::array<std::string, 2> more_strings = { "c", "d" };
std::string another_string = "e";

std::array<std::string, 5> result = {strings, more_strings, another_string};

The same question applies to initializing std::initializer_list with smaller std::initializer_lists.

7
  • 1
    It's not initialization, but you can write a function with a parameter pack that fills the array. Commented Nov 27, 2015 at 10:29
  • 1
    The syntax you tried isn't possible. Of course you could write { strings[0], strings[1], more_strings[0], more_strings[1], another_string } Commented Nov 27, 2015 at 10:29
  • @KarolyHorvath in that case the strings are default-constructed and then assigned, which is a small waste that I suppose OP wants to avoid Commented Nov 27, 2015 at 10:29
  • I think you'll have to move to another container if you want to avoid the problem of default-construction ; array isn't particularly flexible. E.g. you could use vector and use emplacement to add the ranges. But this is probably worse overall than the array. Commented Nov 27, 2015 at 10:31
  • 1
    There is no std::initializer_list in your code. Brace initialisation != std::initializer_list. Commented Nov 27, 2015 at 10:41

2 Answers 2

2

If it's possible for you, you might find tuples easier (and more efficient) to work with until the final creation of the array.

conceptually, you could write something like this:

    using namespace std;
    auto strings = make_tuple( "a"s, "b"s );
    auto more_strings = make_tuple( "c"s, "d"s);
    auto another_string = make_tuple("e"s);
    auto result = to_array(tuple_cat(strings, more_strings, another_string));

Of course you'll need an implementation for to_array which is not trivial. The full code is below, with credit going to Luc Danton in this answer: Convert std::tuple to std::array C++11

full code for reference:

#include <iostream>
#include <string>
#include <tuple>
#include <array>


template<int... Indices>
struct indices {
    using next = indices<Indices..., sizeof...(Indices)>;
};

template<int Size>
struct build_indices {
    using type = typename build_indices<Size - 1>::type::next;
};

template<>
struct build_indices<0> {
    using type = indices<>;
};

template<typename T>
using Bare = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices()
{ return {}; }

template<typename Tuple, int... Indices>
std::array<
typename std::tuple_element<0, Bare<Tuple>>::type,
std::tuple_size<Bare<Tuple>>::value
>
to_array(Tuple&& tuple, indices<Indices...>)
{
    using std::get;
    return {{ get<Indices>(std::forward<Tuple>(tuple))... }};
}

template<typename Tuple>
auto to_array(Tuple&& tuple)
-> decltype( to_array(std::declval<Tuple>(), make_indices<Tuple>()) )
{
    return to_array(std::forward<Tuple>(tuple), make_indices<Tuple>());
}


auto main() -> int
{
    using namespace std;

    auto strings = make_tuple( "a"s, "b"s );
    auto more_strings = make_tuple( "c"s, "d"s);
    auto another_string = make_tuple("e"s);

    auto result = to_array(tuple_cat(strings, more_strings, another_string));
    for (const auto& e : result)
    {
        cout << e << endl;
    }
    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Note that build_indices and indices may be replaced by std::make_index_sequence and std::index_sequence.
1

Here is a function that can join std::arrays.

template<typename T>
auto wrap_value(T&& value)
{
    return std::tuple<T&&>(std::forward<T>(value));
}

template<typename T, std::size_t N>
std::array<T, N>& wrap_value(std::array<T, N>& value)
{
    return value;
}

template<typename T, std::size_t N>
std::array<T, N> const& wrap_value(std::array<T, N> const& value)
{
    return value;
}

template<typename T, std::size_t N>
std::array<T, N>&& wrap_value(std::array<T, N>&& value)
{
    return std::move(value);
}

template<std::size_t... Is, typename... Ts>
std::array<std::common_type_t<Ts...>, sizeof...(Is)>
join_arrays_impl(std::index_sequence<Is...>, std::tuple<Ts...>&& parts)
{
    return {std::get<Is>(std::move(parts))...};
}

template<typename... Ts>
auto join_arrays(Ts&&... parts)
{
    auto wrapped_parts = std::tuple_cat((wrap_value)(std::forward<Ts>(parts))...);
    constexpr auto size = std::tuple_size<decltype(wrapped_parts)>::value;
    std::make_index_sequence<size> seq;
    return (join_arrays_impl)(seq, std::move(wrapped_parts));
}

I don't think you can implement a similar function for std::initializer_lists. See it working here.

Comments

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.