【发布时间】:2021-10-22 10:26:08
【问题描述】:
我正在阅读 cppreference 并且在 std::aligned_storage 的示例中有这个向量/数组类的示例:
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
// IF you want to see possible implementation of aligned storage see link.
// It's very simple and small, it's just a buffer
std::size_t m_size = 0;
public:
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
// construct value in memory of aligned storage
// using inplace operator new
new(&data[m_size]) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// note: needs std::launder as of C++17
// WHY
return *reinterpret_cast<const T*>(&data[pos]);
}
// Delete objects from aligned storage
~static_vector()
{
for(std::size_t pos = 0; pos < m_size; ++pos) {
// note: needs std::launder as of C++17
// WHY?
reinterpret_cast<T*>(&data[pos])->~T();
}
}
};
基本上每个元素所在的每个存储桶/区域/内存地址都是一个字符缓冲区。在存在的每个元素位置,都会在 T 类型的缓冲区中完成新的放置。因此,当返回该缓冲区的位置时,可以将 char 缓冲区强制转换为 T*,因为在那里构造了一个 T。为什么从 C++17 开始需要 std::launder?我已经问了很多关于这个的问题,似乎同意:
alignas(alignof(float)) char buffer [sizeof(float)];
new (buffer) float;// Construct a float at memory, float's lifetime begins
float* pToFloat = buffer; // This is not a strict aliasing violation
*pToFloat = 7; // Float is live here
这显然不是严格的别名违规,完全没问题。那么为什么这里需要 std::launder 呢?
【问题讨论】:
-
您必须使用
new返回的指针来访问创建的对象。不允许投射buffer。std::launder可用于从buffer获取有效指针。 -
附带说明,该评论有些误导。需要使用 std::launder 才能使代码有效。在 c++17 之前缺少所需的工具,使得这样的代码无法合法编写。
-
是的。
char buffer[32]; MyStruct * ptr = new (&buffer[0]) MyStruct{}; ptr->~MyStruct();很好(假设满足对齐和大小要求)。 -
Zebrafish> 是的,添加 std::launder 正是为了有一种合法的方式来编写它,而不是依赖未定义的行为并希望编译器会做正确的事情。
-
这正是我在上面的第二条评论中提到的:该注释具有误导性。在 C++17 之前,没有合法的方式来编写该代码。
标签: c++ undefined-behavior strict-aliasing stdlaunder