请考虑以下示例:
#include <array>
#include <memory>
class trivial
{
public:
trivial() = default;
trivial(int a, float b) : m_a(a), m_b(b) {}
private:
int m_a;
float m_b;
};
template<typename T>
void write(T& arr, size_t idx, int a, float b)
{
::new(static_cast<void*>(std::addressof(arr[idx]))) trivial(a, b);
}
template<typename T>
void destroy(T& arr, size_t idx)
{
std::destroy_at(std::addressof(arr[idx]));
}
int main()
{
auto arr = std::array<trivial, 20>();
write(arr, 3, 10, 20.0f);
destroy(arr, 3);
}
使用位置new
和std::destroy_at
就地安全做任意(但合理)的数据数组?这里是否存在任何风险或潜在的未定义行为,或者是可移植性问题?假设我们不会尝试分配一个被破坏的值,我理解这是未定义的。
我注意到这种方法比使用std::aligned_storage
和reinterpret_cast
更好,主要是因为std::launder
作为优化阻滞剂。如果我对在std::array
中存储我的值的额外限制感到满意(例如需要默认构造函数),这是否是可接受的用例?
你对std::array arr
的第三个元素有双重破坏。一次是通过显式破坏(destroy
调用)而另一种是通过隐式破坏(当arr
超出范围时)。根据C ++标准,这会导致未定义的行为。
15.4析构者[class.dtor] ... 16为对象调用析构函数后,该对象不再存在;如果为生命周期结束的对象调用析构函数,则行为未定义。 [示例:如果显式调用自动对象的析构函数,并且随后以通常调用对象的隐式销毁的方式保留块,则行为未定义。 - 末端的例子]
上述引用中的示例与您尝试执行的操作有些类似。