I proposed several techniques to answer to Idiom for initializing an std::array using a generator function taking the index?.
A classical way to answer would be to use std::index_sequence
-based solution but op had two constraints:
- it should work with a non default-constructible type
- it should work for large
std::array
sizes, larger than what commonstd::index_sequence
implementations are supporting.
Using std::array
for large array instead of a std::vector
is debatable but it’s not the “assignment”.
Some of my answers are relying on creating a temporary raw storage where I’m building objects in place and then I’m aliasing the memory to a pointer to std::array
that I’m using to move-initialized a new array.
I have concerns about the legality of the code, that can be pinned down to:
#include <array>
#include <cstddef>
// allocate storage for an std::array and construct its object inplace before
// moving it to destination
template<typename T, std::size_t N, typename Gen) std::array<T,N> make_array(gen)
{
// checking std::array layout
static_assert(sizeof(std::array<T, N>) == N * sizeof(T));
alignas(T) unsigned char storage[N * sizeof(T)];
for (std::size_t i = 0; i != N; ++i) {
new (storage + i * sizeof(T)) T(gen(i));
}
// aliasing from array of bytes
// is this legal?
return std::move(*reinterpret_cast<std::array<T, N>*>(storage));
}
// allocate storage for an std::array inside a vector and construct its object
// inplace before moving it to destination
template<typename T, std::size_t N, typename Gen) std::array<T,N> make_array(gen)
{
// checking std::array layout
static_assert(sizeof(std::array<T, N>) == N * sizeof(T));
auto v = std::vector<T>{};
v.reserve(N);
for (std::size_t i = 0; i != N; ++i) {
v.emplace_back(gen(i))
};
// aliasing from array of T
// is this legal?
return std::move(
*reinterpret_cast<std::array<T, N>*>(v.data());
}
playground
Are the return statements above valid?
And, if no, is there a way to solve the issue?
Wording from the standard whould be appreciate to justify the answer(s).
1