这种类型的双关语定义明确吗?
Is this type punning well-defined?
阅读 this answer 中关于严格别名规则的引述,我看到以下针对 C++11 的内容:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
...
an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
...
所以我认为下面的代码没有违反严格的别名规则:
#include <iostream>
#include <cstdint>
#include <climits>
#include <limits>
struct PunnerToUInt32
{
std::uint32_t ui32;
float fl;
};
int main()
{
static_assert(std::numeric_limits<float>::is_iec559 &&
sizeof(float)==4 && CHAR_BIT==8,"Oops");
float x;
std::uint32_t* p_x_as_uint32=&reinterpret_cast<PunnerToUInt32*>(&x)->ui32;
*p_x_as_uint32=5;
std::cout << x << "\n";
}
很好,满足严格的别名规则。由于任何其他原因,这是否仍然表现出未定义的行为?
你不能这样做:&reinterpret_cast<PunnerToUInt32*>(&x)
reinterpret_cast
状态的规则:
When a pointer or reference to object whose dynamic type is DynamicType
is reinterpret_cast
(or C-style cast) to a pointer or reference to object of a different type AliasedType
, the cast always succeeds, but the resulting pointer or reference may only be used to access the object if one of the following is true:
AliasedType
is (possibly cv-qualified) DynamicType
AliasedType
and DynamicType
are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same type T
AliasedType
is the (possibly cv-qualified) signed or unsigned variant of DynamicType
AliasedType
is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to obtain a usable pointer to a struct or union given a pointer to its non-static member or element.
AliasedType
is a (possibly cv-qualified) base class of DynamicType
AliasedType
is char
or unsigned char
: this permits examination of the object representation of any object as an array of unsigned char
因为 none 对于 DynamicType
是 float
和 AliasedType
是 PunnerToUInt32
的组合是正确的,指针不能用于访问你正在做的对象。使行为未定义。
有关详细信息,请参阅:
编辑:
分解第 4 个 bullet int bite size 块产量:
- "
AliasedType
"
这里取为PunnerToUInt32
"is an aggregate type or a union type"
PunnerToUInt32
符合条件,因为它符合 aggregate type:
的条件
- array type
- class type (typically,
struct
or union
), that has
- no private or protected non-static data members
- no user-provided constructors, including those inherited from public bases (explicitly defaulted or deleted constructors are allowed)
- no virtual, private, or protected base classes
- no virtual member functions
"which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions)"
再次 PunnerToUInt32
符合条件,因为它是 float fl
成员
- "this makes it safe to obtain a usable pointer to a struct or union"
这是最后正确的部分,因为 AliassedType
是 PunnerToUInt32
- "given a pointer to its non-static member or element"
这是一个违规行为,因为 DynamicType
即 x
不是 PunnerToUInt32
的成员
因为违反第 5 部分操作这个指针是未定义的行为。
如果你喜欢一些推荐的阅读,你可以查看 Empty Base Optimization 如果没有,我会在这里给你主要的相关性:
Empty base optimization is required for StandardLayoutTypes in order to maintain the requirement that the pointer to a standard-layout object, converted using reinterpret_cast
, points to its initial member
因此你可以通过这样做来利用 reinterpret_cast
的第 4 颗子弹:
PunnerToUInt32 x = {13, 42.0F};
auto y = reinterpret_cast<PunnerToUInt32*>(&x.ui32);
如果 p_x_as_uint32
以某种方式指向 x
1,那么 *p_x_as_uint32=5
将通过以下方式访问 float
类型的对象uint32_t
类型的左值,这将导致未定义的行为。
问题中的 "access" 是赋值,重要的是使用的左值类型 (uint32_t
) 和访问对象的实际类型 (float
) .用于获取指针的一系列强制转换是无关紧要的。
值得记住的是,存在严格的别名规则以启用基于类型的别名分析。不管走的路有多折磨,if you can legally "create a situation where an int*
and a float*
can simultaneously exist and both can be used to load or store the same memory, you destroy TBAA"。如果您认为标准的措辞以某种方式允许您这样做,那么您可能错了,但如果您是对的,那么您所发现的只是标准措辞中的一个缺陷。
1 class 成员访问是未定义的行为(由于遗漏),因为没有实际的 PunnerToUInt32
对象。但是,class 成员访问不是严格别名规则意义上的 "access";后者表示 "to read or modify the value of an object".
阅读 this answer 中关于严格别名规则的引述,我看到以下针对 C++11 的内容:
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
...
an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
...
所以我认为下面的代码没有违反严格的别名规则:
#include <iostream>
#include <cstdint>
#include <climits>
#include <limits>
struct PunnerToUInt32
{
std::uint32_t ui32;
float fl;
};
int main()
{
static_assert(std::numeric_limits<float>::is_iec559 &&
sizeof(float)==4 && CHAR_BIT==8,"Oops");
float x;
std::uint32_t* p_x_as_uint32=&reinterpret_cast<PunnerToUInt32*>(&x)->ui32;
*p_x_as_uint32=5;
std::cout << x << "\n";
}
很好,满足严格的别名规则。由于任何其他原因,这是否仍然表现出未定义的行为?
你不能这样做:&reinterpret_cast<PunnerToUInt32*>(&x)
reinterpret_cast
状态的规则:
When a pointer or reference to object whose dynamic type is
DynamicType
isreinterpret_cast
(or C-style cast) to a pointer or reference to object of a different typeAliasedType
, the cast always succeeds, but the resulting pointer or reference may only be used to access the object if one of the following is true:
AliasedType
is (possibly cv-qualified)DynamicType
AliasedType
andDynamicType
are both (possibly multi-level, possibly cv-qualified at each level) pointers to the same typeT
AliasedType
is the (possibly cv-qualified) signed or unsigned variant ofDynamicType
AliasedType
is an aggregate type or a union type which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions): this makes it safe to obtain a usable pointer to a struct or union given a pointer to its non-static member or element.AliasedType
is a (possibly cv-qualified) base class ofDynamicType
AliasedType
ischar
orunsigned char
: this permits examination of the object representation of any object as an array ofunsigned char
因为 none 对于 DynamicType
是 float
和 AliasedType
是 PunnerToUInt32
的组合是正确的,指针不能用于访问你正在做的对象。使行为未定义。
有关详细信息,请参阅:
编辑:
分解第 4 个 bullet int bite size 块产量:
- "
AliasedType
"
这里取为PunnerToUInt32
"is an aggregate type or a union type"
的条件PunnerToUInt32
符合条件,因为它符合 aggregate type:- array type
- class type (typically,
struct
orunion
), that has- no private or protected non-static data members
- no user-provided constructors, including those inherited from public bases (explicitly defaulted or deleted constructors are allowed)
- no virtual, private, or protected base classes
- no virtual member functions
"which holds one of the aforementioned types as an element or non-static member (including, recursively, elements of subaggregates and non-static data members of the contained unions)"
再次PunnerToUInt32
符合条件,因为它是float fl
成员- "this makes it safe to obtain a usable pointer to a struct or union"
这是最后正确的部分,因为AliassedType
是PunnerToUInt32
- "given a pointer to its non-static member or element"
这是一个违规行为,因为DynamicType
即x
不是PunnerToUInt32
的成员
因为违反第 5 部分操作这个指针是未定义的行为。
如果你喜欢一些推荐的阅读,你可以查看 Empty Base Optimization 如果没有,我会在这里给你主要的相关性:
Empty base optimization is required for StandardLayoutTypes in order to maintain the requirement that the pointer to a standard-layout object, converted using
reinterpret_cast
, points to its initial member
因此你可以通过这样做来利用 reinterpret_cast
的第 4 颗子弹:
PunnerToUInt32 x = {13, 42.0F};
auto y = reinterpret_cast<PunnerToUInt32*>(&x.ui32);
如果 p_x_as_uint32
以某种方式指向 x
1,那么 *p_x_as_uint32=5
将通过以下方式访问 float
类型的对象uint32_t
类型的左值,这将导致未定义的行为。
问题中的 "access" 是赋值,重要的是使用的左值类型 (uint32_t
) 和访问对象的实际类型 (float
) .用于获取指针的一系列强制转换是无关紧要的。
值得记住的是,存在严格的别名规则以启用基于类型的别名分析。不管走的路有多折磨,if you can legally "create a situation where an int*
and a float*
can simultaneously exist and both can be used to load or store the same memory, you destroy TBAA"。如果您认为标准的措辞以某种方式允许您这样做,那么您可能错了,但如果您是对的,那么您所发现的只是标准措辞中的一个缺陷。
1 class 成员访问是未定义的行为(由于遗漏),因为没有实际的 PunnerToUInt32
对象。但是,class 成员访问不是严格别名规则意义上的 "access";后者表示 "to read or modify the value of an object".