【发布时间】:2022-08-15 11:44:55
【问题描述】:
我正在尝试创建一个 constexpr 友好的小型缓冲区优化向量类型,它像往常一样存储开始、结束和容量指针,但是当它默认构造时,开始和容量指针指向本地内存,然后在需要时重新分配到堆。但是我无法弄清楚如何在 constexpr 上下文中存储容量指针,因为我希望它能够支持非平凡构造的类型。为了存储非平凡构造的类型,我不能使用 std::aligned_storage 因为完整的类型擦除不允许我以后获取元素(因为不允许 reinterpret_cast ),所以我决定将元素存储在数组中工会(有点像可选的)。像这样存储它可以让我稍后通过联合访问获取元素,并允许我保持数组未初始化,但是当它存储在联合内部时,我无法弄清楚如何存储指向容量结束的值指针,因为它没有检测到指向联合值时,整个指针超出最后一个指针的内容。当然,所有这些都需要 c++20。
#include <algorithm>
#include <memory>
#include <utility>
#include <array>
struct Null{};
template<typename T>
union Storage
{
Null uninitialized;
T value;
constexpr Storage()
: uninitialized{}
{}
template<typename... Args>
constexpr Storage(Args&&... args)
: value(std::forward<Args>(args)...)
{}
constexpr ~Storage(){}
};
template<typename T, size_t N>
struct Vec
{
std::array<Storage<T>, N> storage;
T* begin;
T* end;
constexpr Vec()
: begin(makeBegin())
, end(makeEnd())
{}
constexpr T* makeBegin()
{
return &storage[0].value;
}
constexpr T* makeEnd()
{
return (&storage[N].value);
}
};
constexpr bool test()
{
Vec<std::pair<float, float>, 10> vec{};
for(auto it = vec.begin; it < vec.end; ++it)
{
std::construct_at(it, 10.0f, 10.0f);
}
return vec.begin[5] == std::pair{ 10.0f, 10.0f };
}
int main()
{
static_assert(test());
}
https://godbolt.org/z/46o19qcvP
是否有另一种方法可以获取指向存储的非平凡可构造类型的指针,例如数组中的对而不初始化它们?
-
(1) 任何地方都没有
T的数组,所以通过Vec<T>::begin的指针运算和索引是UB。 (2) 允许实现向Storage添加它认为合适的填充。option不在乎,但你不能便携地使用相同的技巧来制作数组。 -
我当然可以将指向联合本身的指针保留为迭代器,但是与后来使用的堆内存迭代器结合起来会有点麻烦,以防它溢出。有什么办法可以将它存储为 T 指针?
-
我不认为这是可行的。
-
我不确定您为什么要使用
Storageunion而不是std::variant,其中定义的第一个类型是std::monostate。 -
Variant 也存储了一个我不需要的类型索引。但是,如果我有一个变体数组,我仍然无法让迭代器指向 T。似乎在 constexpr 中使用未初始化的数组比使用 constexpr new 更难......这将迫使我编写 2 个实现,一个用于 consteval,一个用于非 consteval
标签: c++ arrays c++20 constexpr unions