Rap*_*tor 28 c++ strict-aliasing undefined-behavior language-lawyer type-punning
我目前正在使用aligned_storage来实现类似于boost :: optional的'Optional'类型.要做到这一点,我有一个类成员,如下所示:
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type t_;
Run Code Online (Sandbox Code Playgroud)
我使用placement new来创建对象,但是我不存储返回任何地方的指针.相反,我在所有我的成员函数中访问对象的基础类型(显然通过检查确保对象通过也存储在我的Optional类型中的布尔标志有效):
T const* operator->() const {
return static_cast<T const*>(static_cast<void const*>(&t_));
}
Run Code Online (Sandbox Code Playgroud)
我的问题是这是否安全.我的理解是我对placement new的使用改变了对象的'动态类型',只要我继续使用该类型访问内存,我就没问题.但是我不清楚我是否必须保持从placement new返回的指针,或者我是否只允许在需要访问它时转换为底层类型.我已经阅读了C++ 11标准的第3.10节,但是我在标准方面还不够流利,无法确定.
如果可能的话,如果你能在答案中提到标准,我会感觉更好(这有助于我在晚上睡觉:P).
Joe*_*rgB 13
ABICT您的使用是安全的.
§5.3.4/ 10说:
new-expression将请求的空间量传递给分配函数,作为std :: size_t类型的第一个参数.该参数不得小于正在创建的对象的大小; 仅当对象是数组时,它可能大于正在创建的对象的大小.
对于非数组对象,分配的大小不能大于对象的大小,因此对象表示必须从分配的内存的开头开始才能适合.
作为"分配"的结果,Placement new返回传入的指针(参见§18.6.1.3/2),因此构造对象的对象表示将从该地址开始.
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<>
了显式转换,其中隐式转换与隐式转换具有相同的效果:
否则,对于某些发明的临时变量(8.5),如果声明 格式正确,则
e
可以T
使用static_cast
表单的表达式将表达式显式转换为类型.这种显式转换的效果与执行声明和初始化,然后使用临时变量作为转换结果相同.[...]static_cast<T>(e)
T t(e);
t
对于逆static_cast<>
(从void*
到T*
),§5.2.9/ 13状态:
类型为"指向cv1 void的指针"的prvalue可以转换为类型为"指向cv2 T的指针"的prvalue,其中T是对象类型,cv2与cv1具有相同的cv资格,或者更高的cv资格.[...]指向对象的类型指针的值转换为"指向cv void的指针"并返回,可能具有不同的cv-qualification,应具有其原始值.
因此,如果您void*
指向T
对象的存储(这是将a隐式转换为T*
对象所导致的指针值,那么a中static_cast
的一个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;
Run Code Online (Sandbox Code Playgroud)
然后
*pT
完全覆盖存储的第一个大小(T)字节t_
,这样pvT == pvt_
pvt_ == static_cast<void*>(&t_)
static_cast<T*>(pvT) == pT
static_cast<T*>(static_cast<void*>(&t_)) == pT