How can I initialize an std::array
from a range (as defined by a pair of iterators)?
Something like this:
vector<T> v; ... // I know v has exactly N elements (e.g. I just called v.resize(N)) // Now I want a initialized with those elements array<T, N> a(???); // what to put here?
I thought array
would have a constructor taking a pair of iterators, so that I could do array<T, N> a(v.begin(), v.end())
, but it appears to have no constructors at all!
I know I can copy
the vector into the array, but I'd rather initialize the array with the vector contents directly, without default-constructing it first. How can I?
With random access iterators, and assuming a certain size at compile-time, you can use a pack of indices to do so:
template <std::size_t... Indices> struct indices { using next = indices<Indices..., sizeof...(Indices)>; }; template <std::size_t N> struct build_indices { using type = typename build_indices<N-1>::type::next; }; template <> struct build_indices<0> { using type = indices<>; }; template <std::size_t N> using BuildIndices = typename build_indices<N>::type; template <typename Iterator> using ValueType = typename std::iterator_traits<Iterator>::value_type; // internal overload with indices tag template <std::size_t... I, typename RandomAccessIterator, typename Array = std::array<ValueType<RandomAccessIterator>, sizeof...(I)>> Array make_array(RandomAccessIterator first, indices<I...>) { return Array { { first[I]... } }; } // externally visible interface template <std::size_t N, typename RandomAccessIterator> std::array<ValueType<RandomAccessIterator>, N> make_array(RandomAccessIterator first, RandomAccessIterator last) { // last is not relevant if we're assuming the size is N // I'll assert it is correct anyway assert(last - first == N); return make_array(first, BuildIndices<N> {}); } // usage auto a = make_array<N>(v.begin(), v.end());
This assumes a compiler capable of eliding the intermediate copies. I think that assumption is not a big stretch.
Actually, it can be done with input iterators as well, since the computation of each element in a braced-init-list is sequenced before the computation of the next element (§8.5.4/4).
// internal overload with indices tag template <std::size_t... I, typename InputIterator, typename Array = std::array<ValueType<InputIterator>, sizeof...(I)>> Array make_array(InputIterator first, indices<I...>) { return Array { { (void(I), *first++)... } }; }
Since *first++
doesn't have any I
in it, we need a dummy I
to provoke the pack expansion. Comma operator to the rescue, with void()
to silence warnings about lack of effects, and also preventing overloaded commas.
Like you have noticed, std::array has no constructors at all (except for the compiler generated default constructor).
This was done on purpose, so it can be statically initialized just like a C array. If you want to fill the array without a static initializer, you will have to copy your data.
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