在固定的,无序的,拥有的数组中安全,惯用的销毁和压缩
Safe, idiomatic destroy-and-compact within fixed, unordered, owning array
考虑一个数据结构,该数据结构包含固定尺寸的缓冲区,该缓冲区拥有某些任意非平凡类型的现场成员。缓冲区是无序的,但其内容是使用固定数组和计数器值连续存储的。破坏该数组中元素并用数组的最后一个元素替换其插槽以保持连续性的现代,惯用的C 方法是什么?我特别关心确保元素被适当破坏,并且替换被正确有效地移动。不幸的是,std::vector
不是这里的选项。
这是我能想到的:
void destroy_replace(
std::array<arbitrary, 20>& arr,
size_t& count,
size_t index)
{
SOME_ASSERT_MACRO(index < count);
std::destroy_at(std::addressof(arr.at(index)));
arr[index] = std::move(arr.at(count - 1));
--count;
}
这是正确的吗?特别是std::destroy_at
?
需要举动吗?我应该/应该作为安置新移动构造函数吗?
操作顺序相对于例外保证了吗?
您应该使用位置新的移动构造函数,作为破坏对象的分配操作员,不确定。如果您没有这样的构造函数,则可以诉诸默认的构造函数,然后可以构建移动分配,但是应构造std::destroy_at
之后的对象。
需要移动才能投入到RVALUE参考,因为.at
是lvalue参考。
在这种情况下,提供异常保证的典型方法是需要移动构造函数或移动分配为noexcept
。否则它不适合移动,您需要复制。
只需从最后一个元素移动分配即可。这并不是说您的代码实际上正在销毁网络上的任何内容(固定后 - 您必须在破坏元素后使用新的位置,因此您要销毁一个并创建另一个),而移动分配通常至少具有高效 - 如果没有多于销毁和重建。
有两种方法。您要么移动要删除要删除的元素,然后销毁最后一个,或然后移动删除最后一个元素。
template<class T, std::size_t N>
struct pseudo_array {
using raw = std::aligned_storage_t<sizeof(T), alignof(T)>;
std::array<raw, N> data;
std::size_t highwater = 0;
void erase( std::size_t i ) {
std::launder( (T*)(data.data()+i) )->~T();
--highwater;
if (i != highwater) {
auto* ptr_last = std::launder( (T*)(data.data()+highwater) );
::new( (void*)(data.data()+i) ) T( std::move(*ptr_last ) );
ptr_last->~T();
}
}
// add const version
T& operator[](std::size_t i) {
return *std::launder( (T*)(data.data()+i) );
}
std::size_t size() const { return highwater; }
template<class...Args>
T& emplace( Args&&...args ) {
void* where = (void*)(data.data()+highwater);
T* ptr_elem = ::new(where) T(std::forward<Args>(args)...);
++highwater;
return *ptr_elem;
}
// Care taken to keep invariants true while we destroy
~pseudo_array() {
while(highwater > 0) {
--highwater;
std::launder( (T*)(data.data()+highwater) )->~T();
}
}
};
替代擦除:
void erase( std::size_t i ) {
auto* ptr_last = std::launder( (T*)(data.data()+highwater-1) );
auto* ptr_target = std::launder( (T*)(data.data()+i) );
if (ptr_last!=ptr_target) {
*ptr_target = std::move(*ptr_last);
}
--highwater; // this goes before dtor, in case dtor throws
ptr_last->~T();
}
相关文章:
- 从不同线程使用int64的不同字节安全吗
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- C++中高效的大型稀疏块压缩线性方程
- 嵌入方指针压缩已禁用
- C++使用整数的压缩数组初始化对象
- 虚拟决赛作为安全
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 如何将元素添加到数组的线程安全函数?
- 在C++中将函数压缩为两种方式
- C++中的线程安全删除
- 通过网络、跨平台传递std::变体是否安全
- 在C++中使用LZ4压缩目录
- 在std::thread中,joinable()然后join()线程安全吗
- 使用std::istream::peek()总是安全的吗
- 从值小于256的uint16到uint8的Endian安全转换
- 使用C++进行运行长度解压缩
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 在c++队列中使用pop和visit实现线程安全
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 在固定的,无序的,拥有的数组中安全,惯用的销毁和压缩