Suppose I have a std::vector
of a size known at compile time, and I want to turn that into an std::array
. How would I do that? Is there a standard function to do this?
The best solution I have so far is this:
template<class T, std::size_t N, class Indexable, std::size_t... Indices>
std::array<T, N> to_array_1(const Indexable& indexable, std::index_sequence<Indices...>) {
return {{ indexable[Indices]... }};
}
template<class T, std::size_t N, class Indexable>
std::array<T, N> to_array(const Indexable& indexable) {
return to_array_1<T, N>(indexable, std::make_index_sequence<N>());
}
std::array<Foo, 123> initFoos {
std::vector<Foo> lst;
for (unsigned i = 0; i < 123; ++i)
lst.push_back(getNextFoo(lst));
return to_array<Foo, 123>(lst); // passing lst.begin() works, too
}
The application is similar to Populate std::array with non-default-constructible type (no variadic templates): I too have a type which is not default-constructible, so I need to compute the actual values by the time the array gets initialized. However contrary to that question, for me the values are not merely a function of the index, but also of the preceding values. I can build my values much more easily using a loop than a series of function calls. So I construct the elements in a loop and place them in a vector, then I want to use the final state of that vector to initialize the array.
The above seems to compile and work fine, but perhaps there are ways to improve it.
operator[]
, and instead use forward iterator semantics only so it would work for std::set
or std::forward_list
as input, too.std::vector
and instead express my goal using std::array<std::optional<T>, N>
instead, using C++17 or some equivalent implementation.Related questions:
You can access a vector as an array.
Therefore, array of vectors is two dimensional array with fixed number of rows where each row is vector of variable length. Each index of array stores a vector which can be traversed and accessed using iterators. Insertion: Insertion in array of vectors is done using push_back() function.
std::array contains a built-in array, which can be initialized via an initializer list, which is what the inner set is. The outer set is for aggregate initialization.
I would propose:
template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{ ((void)Is, *iter++)... }};
}
template<std::size_t N, typename Iter,
typename T = typename std::iterator_traits<Iter>::value_type>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
return to_array<T>(iter, std::make_index_sequence<N>{});
}
This deduces the element type from the iterator and leaves copy-vs-move semantics up to the caller – if the caller wants to move, they can opt-in via std::move_iterator
or the like:
auto initFoos() {
constexpr unsigned n{123};
std::vector<Foo> lst;
for (unsigned i{}; i != n; ++i) {
lst.push_back(getNextFoo(lst));
}
// copy-init array elements
return to_array<n>(lst.cbegin());
// move-init array elements
return to_array<n>(std::make_move_iterator(lst.begin()));
}
Online Demo
EDIT: If one wants to override the deduced element type, as indicated in the comments, then I propose:
template<typename T, typename Iter, std::size_t... Is>
constexpr auto to_array(Iter& iter, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{ ((void)Is, T(*iter++))... }};
}
template<std::size_t N, typename U = void, typename Iter,
typename V = typename std::iterator_traits<Iter>::value_type,
typename T = std::conditional_t<std::is_same<U, void>{}, V, U>>
constexpr auto to_array(Iter iter)
-> std::array<T, N> {
return to_array<T>(iter, std::make_index_sequence<N>{});
}
This leaves the element type optional but makes it the second parameter rather than the first, so usage would look like to_array<N, Bar>(lst.begin())
rather than to_array<Bar, N>(lst.begin())
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With