o_o*_*tle 6 c++ strict-aliasing language-lawyer c++20
此示例是从cppreference复制的。
struct Y { int z; };
alignas(Y) std::byte s[sizeof(Y)];
Y* q = new(&s) Y{2};
const int f = reinterpret_cast<Y*>(&s)->z; // Class member access is undefined
// behavior: reinterpret_cast<Y*>(&s)
// has value "pointer to s" and does
// not point to a Y object
const int g = q->z; // OK
const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
Run Code Online (Sandbox Code Playgroud)
我想知道像s[0] = std::byte{0}
上面的语句之后添加操作是否是未定义的行为?看起来它并没有违反严格的别名规则,因为std::byte
根据cppreference可以是任何类型的“AliasedType” ,这意味着将任何对象视为字节数组是合法的。
请注意,我添加了 c++20 标签,因为它们可能只有在 C++20 之后才被明确定义。
感谢@LanguageLawyer 的更正,我更新了我的答案。
我想知道在上面的语句之后添加像 s[0] = std::byte{0} 这样的操作是否是未定义的行为?
我相信这是一种未定义的行为,但其原因与严格的别名规则无关。D
严格的别名规则规定,如果程序通过类型的左值访问类型的对象T
,但D
和T
不“相似”并且T
不是那些特殊字符类型之一,则程序具有未定义的行为。在您的情况下,您std::byte
通过 type 的泛左值访问对象std::byte
,这在严格的别名规则下完全没问题。
这里真正的问题是s
还为另一个对象提供存储Y
。一旦s
为对象提供了存储Y
(在 new 表达式之后),数组元素的生命周期就会终止,因为它们的存储被重用:
[基本生活]/1.2:
T 类型的对象 o 的生命周期在以下情况结束:
- 如果 T 是非类类型,则该对象被销毁,或者
- 如果 T 是类类型,则析构函数调用开始,或者
- 对象占用的存储被释放,或者被未嵌套在 o ([intro.object]) 中的对象重用。
因此,如果您在 new 表达式之后访问s[0]
,您将访问一个过期对象,这是一种未定义的行为。