正如标题所说,我想知道在数组中移动元素的正确方法是什么:
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> data;
操作简单吗?
data[dst] = data[src];
还是我需要添加诸如move之类的其他东西,因为其存储尚未初始化,我是否需要使用copy或move构造函数,如:
new (&data[dst]) T(std::move(data[src]));
由于data [src]不是正确的类型T,我需要改为:
new (&data[dst]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[src])));
我正在寻找最灵活的方式来移动T物品,包括仅移动类型等。
基本上,我正在创建一个压缩数组,该数组始终将元素移动到内存中是连续的,即使为了防止在数组的活动部分中出现孔而删除了它们。
编辑: 由于评论只需要一个最小的例子,我猜是这样的:
template<class T, std::size_t N>
class SimpleExampleClass {
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> data;
public:
void move_element(std::size_t src, std::size_t dst) {
// data[dst] = data[src]; ?
// or
// new (&data[dst]) T(std::move(data[src]));
// or
// new (&data[dst]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[src])));
// or
// something else?
// then I would need some way to clean up the src element, not sure what would suffice for that.
// as calling a destructor on it could break something that was moved potentially?
}
// Other functions to manipulate the data here... (example below)
template<typename ...Args>
void emplace_push(Args&&... args) noexcept {
new (&data[/*some index*/]) T(std::forward<Args>(args)...);
}
void push(T item) noexcept {
emplace_push(std::move(item));
}
};
答案 0 :(得分:3)
std::aligned_storage
本身只是字节的集合。没有什么可动的,std::move(data[src])
只是一个禁忌。您应该首先使用new放置来创建对象,然后可以通过在新位置进行移动构造来移动该对象。
简单的例子:
auto ptr = new (&data[0]) T();
new (&data[1]) T(std::move(*ptr));
std::destroy_at(ptr);
在
T
是unique_ptr
之类的情况下,或在其他类似的边缘情况下,正确调用旧元素索引上的destroy应该不会有问题吗?
从对象移开会使其处于某种有效状态,并且仍然必须销毁该对象。
由于
data[0]
只是字节的集合,指向它的指针会起作用,还是需要在移动构造函数中使用该指针之前将其重新转换为强制转换?
如果用reinterpret_cast
和std::launder
装饰,就可以了,就像您在问题中所写的一样:
new (&data[1]) T(std::move(*std::launder(reinterpret_cast<T*>(&data[0]))));
标准库包含一些有用的函数,用于处理未初始化的内存。完整列表可以在here中找到(请参见未初始化的存储部分)。