aligned_storage和严格别名

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您的使用是安全的.

  • 放置类型为T的对象的new将创建一个从传入的地址开始的对象.

§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

  • "ABICT"代表什么? (6认同)
  • “尽我所能”,ABICT? (3认同)