std::aligned_storage 的目的是什么?
What is the purpose of std::aligned_storage?
如果我理解正确的话,std::aligned_storage
的主要优点是它管理对齐。它也可以用 memcpy()
复制,并且可以与 POD 类型一起使用。
但是!
1) POD 类型默认由编译器对齐,我们可以使用 #pragma pack(push, 1)
覆盖此编译器的对齐方式
2) 默认情况下我们可以用memcpy()
复制POD(我们不应该为这个能力做些什么)
所以我不太明白为什么我们需要std::aligned_storage
?
只要您希望将内存分配与对象创建分离,就可以使用 std::aligned_storage
。
您声称:
Also it is usable only with POD types.
但这不是真的。没有什么可以阻止 std::aligned_storage
与非 POD 类型一起使用。
The example on cppreference 提供合法用例:
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];
std::size_t m_size = 0;
...
这里的想法是,一旦构造了static_vector
,就会立即为N
个T
类型的对象分配内存,但不会分配T
类型的对象尚未创建。
你不能用一个简单的 T data[N];
数组成员来做到这一点,因为这会立即为每个元素 运行 T
构造函数,或者如果 T
不可默认构造。
首先,#pragma
指令不可移植。该标准没有定义任何必须支持的强制性编译指示,因此每个编译器都可以自由定义自己的集合。但是无论您使用什么编译器,std::aligned_storage
都需要简单地工作。编译器库编写者可能会根据编译指示、属性或编译器扩展来定义它,但用户可以 #include <type_traits>
并开始使用它。
"it is usable only with POD types" 并非如此。事实上,使用 aligned_storage
的一种常见方式是作为一块内存,可以手动创建和销毁 any 类型的其他对象。它或类似的东西可用于实现 std::optional
and std::variant
.
之类的东西
为了展示这背后的想法,这里开始写一个类似于std::optional
的class:
#include <type_traits>
#include <memory>
template <typename T>
class my_optional
{
private:
std::aligned_storage_t<sizeof(T), alignof(T)> m_storage;
bool m_valid;
public:
constexpr my_optional() noexcept : m_valid(false) {}
constexpr my_optional(const T& obj)
noexcept(std::is_nothrow_copy_constructible<T>::value)
: m_valid(false)
{
new(static_cast<void*>(&m_storage)) T(obj);
m_valid = true;
}
constexpr const T& operator*() const
{
return *static_cast<const T*>(static_cast<const void*>(&m_storage));
}
constexpr T& operator*()
{
return *static_cast<T*>(static_cast<void*>(&m_storage));
}
~my_optional()
{
if (m_valid)
operator*().~T();
}
// Much more, to obey the Rule Of Five and add more functionality...
};
std::aligned_storage
管理对齐存储。在存储中放置 POD 对象还是非 POD 对象都无关紧要。
std::aligned_storage
的目的是它提供了一个标准化的高级实用程序来管理对齐存储,这样您就可以编写更简洁的代码,减少麻烦。
如果我理解正确的话,std::aligned_storage
的主要优点是它管理对齐。它也可以用 memcpy()
复制,并且可以与 POD 类型一起使用。
但是!
1) POD 类型默认由编译器对齐,我们可以使用 #pragma pack(push, 1)
2) 默认情况下我们可以用memcpy()
复制POD(我们不应该为这个能力做些什么)
所以我不太明白为什么我们需要std::aligned_storage
?
只要您希望将内存分配与对象创建分离,就可以使用 std::aligned_storage
。
您声称:
Also it is usable only with POD types.
但这不是真的。没有什么可以阻止 std::aligned_storage
与非 POD 类型一起使用。
The example on cppreference 提供合法用例:
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]; std::size_t m_size = 0; ...
这里的想法是,一旦构造了static_vector
,就会立即为N
个T
类型的对象分配内存,但不会分配T
类型的对象尚未创建。
你不能用一个简单的 T data[N];
数组成员来做到这一点,因为这会立即为每个元素 运行 T
构造函数,或者如果 T
不可默认构造。
首先,#pragma
指令不可移植。该标准没有定义任何必须支持的强制性编译指示,因此每个编译器都可以自由定义自己的集合。但是无论您使用什么编译器,std::aligned_storage
都需要简单地工作。编译器库编写者可能会根据编译指示、属性或编译器扩展来定义它,但用户可以 #include <type_traits>
并开始使用它。
"it is usable only with POD types" 并非如此。事实上,使用 aligned_storage
的一种常见方式是作为一块内存,可以手动创建和销毁 any 类型的其他对象。它或类似的东西可用于实现 std::optional
and std::variant
.
为了展示这背后的想法,这里开始写一个类似于std::optional
的class:
#include <type_traits>
#include <memory>
template <typename T>
class my_optional
{
private:
std::aligned_storage_t<sizeof(T), alignof(T)> m_storage;
bool m_valid;
public:
constexpr my_optional() noexcept : m_valid(false) {}
constexpr my_optional(const T& obj)
noexcept(std::is_nothrow_copy_constructible<T>::value)
: m_valid(false)
{
new(static_cast<void*>(&m_storage)) T(obj);
m_valid = true;
}
constexpr const T& operator*() const
{
return *static_cast<const T*>(static_cast<const void*>(&m_storage));
}
constexpr T& operator*()
{
return *static_cast<T*>(static_cast<void*>(&m_storage));
}
~my_optional()
{
if (m_valid)
operator*().~T();
}
// Much more, to obey the Rule Of Five and add more functionality...
};
std::aligned_storage
管理对齐存储。在存储中放置 POD 对象还是非 POD 对象都无关紧要。
std::aligned_storage
的目的是它提供了一个标准化的高级实用程序来管理对齐存储,这样您就可以编写更简洁的代码,减少麻烦。