我目前正在使用aligned_storage来实现类似于boost :: optional的'Optional'类型。为了实现这一点,我有一个像这样的班级成员:
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
我使用placement new来创建对象,但是我不存储返回任何地方的指针。相反,我在所有我的成员函数中访问对象的基础类型(显然通过检查确保对象通过也存储在我的Optional类型中的布尔标志有效):
T const* operator->() const {
return static_cast<T const*>(static_cast<void const*>(&t_));
}
我的问题是这是否安全。我的理解是我对placement new的使用改变了对象的“动态类型”,只要我继续使用该类型访问内存,我就没问题。但是我不清楚我是否必须保持从placement new返回的指针,或者我是否只允许在需要访问它时转换为底层类型。我已经阅读了C ++ 11标准的第3.10节,但是我在标准方面还不够流畅,无法确定。
如果可能的话,如果你能在答案中提及标准,我会感觉更好(这有助于我晚上睡觉:P)。
答案 0 :(得分:13)
ABICT您的使用是安全的。
§5.3.4/ 10说:
new-expression传递请求的空间量 allocation函数作为std :: size_t类型的第一个参数。那 参数应不小于正在创建的对象的大小; 它可能大于正在创建的对象的大小 该对象是一个数组。
对于非数组对象,分配的大小不能大于对象的大小,因此对象表示必须从分配的内存的开头开始才能适合。
Placement new返回传入的指针(参见§18.6.1.3/2)作为“allocation”的结果,因此构造对象的对象表示将从该地址开始。
static_cast<>
并且T*
类型和void*
之间的隐式转换在指向对象的指针和指向其存储的指针之间进行转换(如果对象是完整对象)。§4.10/ 2说:
类型为“指向cv T的指针”的prvalue,其中T是对象类型,可以是 转换为“指向cv void的指针”类型的prvalue。的结果 将“指向cv T的指针”转换为“指向cv void的指针”指向 T类对象所在的存储位置的开头,如 如果对象是类型为T [...]
的派生程度最高的对象(1.8)
这定义了要转换的隐式转换。进一步§5.2.9[expr.static.cast] / 4为显式转换定义static_cast<>
,其中隐式转换与隐式转换具有相同的效果:
否则,表达式
e
可以显式转换为类型T
如果声明,使用static_cast
形式的static_cast<T>(e)
对于一些发明的临时变量T t(e);
(8.5),t
格式正确。 这种显式转换的效果与执行相同 声明和初始化然后使用临时 变量作为转换的结果。 [...]
对于倒数static_cast<>
(从void*
到T*
),§5.2.9/ 13声明:
“指向cv1 void的指针”类型的prvalue可以转换为prvalue 类型为“指向cv2 T的指针”,其中T是对象类型,cv2是 与cv1相同的cv资格,或更高的cv资格。 [...] 指向对象的类型指针值转换为“指向cv void的指针” 并且,可能具有不同的cv资格,应该有它的 原始价值。
因此,如果你有一个void*
指向T
对象的存储(这是由T*
隐式转换为对象而产生的指针值,那么static_cast
的{{1}}将产生指向该对象的有效指针。
回到你的问题,前面提到的意思是,如果你有
T*
然后
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
void * pvt_ = &t_;
T* pT = new (&t_) T(args...);
void * pvT = pT;
的存储空间恰好覆盖*pT
存储空间的第一个大小(T)字节,以便t_
pvT == pvt_
pvt_ == static_cast<void*>(&t_)
static_cast<T*>(pvT) == pT