我提出了几种技术来回答使用获取索引的生成器函数初始化 std::array 的惯用语?.
一个经典的回答方法是使用基于
std::index_sequence
的解决方案,但 op 有两个限制:
std::array
尺寸,大于常见的 std::index_sequence
实现所支持的尺寸。对大型数组使用
std::array
而不是 std::vector
是有争议的,但这不是“赋值”。
我的一些答案依赖于创建一个临时原始存储,我在其中构建对象,然后将内存别名为指向
std::array
的指针,我用它来移动初始化一个新数组。
我对代码的合法性感到担忧,这可以归结为:
#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());
}
上面的返回语句有效吗?
如果没有,有办法解决这个问题吗?
请使用标准中的措辞来证明答案的合理性。
我认为这是 C++20 后合法的,因为
std::array<T, N>
的实例是隐式创建的。
但是,如果您有
C++20
那么您可以使用 std::bit_cast
,这比 reinterpret_cast
的可疑性要低得多:
// 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 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));
}
return std::bit_cast<std::array<T, N>>(storage);
}
如果你有 C++23,你可以用
bit_cast
:做得更好(除非
std::start_lifetime_as
中的副本被省略)
// 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 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));
}
return *std::start_lifetime_as<std::array<T, N>>(storage);
}