Oli*_*liv 5 c++ strict-aliasing unions language-lawyer class-members
在c ++标准中,在[basic.lval] /11.6中说:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[...]
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),[...]
这句话是严格别名规则的一部分.
它可以允许我们访问非现有联盟的非活动成员吗?如:
struct A{
int id :1;
int value :32;
};
struct Id{
int id :1;
};
union X{
A a;
Id id_;
};
void test(){
A a;
auto id = reinterpret_cast<X&>(a).id_; //UB or not?
}
Run Code Online (Sandbox Code Playgroud)
注意:对标准中我没有掌握的内容进行解释,以及为什么上面的例子可能有用.
我想知道[basic.lval] /11.6有什么用处.
[class.mfct.non-static]/2禁止我们调用"已转换为"联合或聚合的成员函数:
如果为非X类型的对象或从X派生的类型调用类X的非静态成员函数,则行为未定义.
考虑到静态数据成员访问,或静态成员函数可以直接使用qualified-name(a_class::a_static_member)执行,唯一有用的用例[basic.lval] /11.6,可能是访问"casted to"union的成员.我想过使用这个最后的标准规则来实现"优化变体".此变体可以包含A类对象或B类对象,这两个对象以大小为1的位域开头,表示类型:
class A{
unsigned type_id_ :1;
int value :31;
public:
A():type_id_{0}{}
void bar{};
void baz{};
};
class B{
unsigned type_id_ :1;
int value :31;
public:
B():type_id_{1}{}
int value() const;
void value(int);
void bar{};
void baz{};
};
struct type_id_t{
unsigned type_id_ :1;
};
struct AB_variant{
union {
A a;
B b;
type_id_t id;};
//[...]
static void foo(AB_variant& x){
if (x.id.type_id_==0){
reinterpret_cast<A&>(x).bar();
reinterpret_cast<A&>(x).baz();
}
else if (x.id.type_id_==1){
reinterpret_cast<B&>(x).bar();
reinterpret_cast<B&>(x).baz();
}
}
};
Run Code Online (Sandbox Code Playgroud)
调用AB_variant::foo不调用未定义的行为,只要它的参数是指类型的对象AB_variant由于规则指针interconvertibility [basic.compound/4.到非活动联合成员访问type_id_被允许的,因为id属于公共初始序列的A,B并type_id_t [class.mem]/25:
但是如果我尝试使用类型的完整对象来调用它会发生什么A?
A a{};
AB_variant::foo(reinterpret_cast<AB_variant&>(a));
Run Code Online (Sandbox Code Playgroud)
这里的问题是我尝试访问不存在的联合的非活动成员.
两个相关的标准段落是[class.mem]/25:
在具有struct类型T1的活动成员的标准布局并集中,允许读取结构类型T2的另一个union成员的非静态数据成员m,前提是m是T1和T2的公共初始序列的一部分; 行为就像提名T1的相应成员一样.
在联合中,如果非静态数据成员的名称是指其生命周期已开始但尚未结束的对象,则该成员处于活动状态.
问题3:表达"其名称是否"表示"一个对象"实际上是一个生活联盟内的对象?或者它a可以引用对象,因为[basic.lval] /11.6.
有很多情况,特别是涉及类型双关和联合,其中 C 或 C++ 标准的一部分描述了某些操作的行为,另一部分将重叠的操作类描述为调用 UB,并且重叠区域包括一些应该执行的操作。由所有实现以及其他至少在某些实现上支持不切实际的实现一致地进行处理。该标准的作者并没有试图完全描述所有应该被视为已定义的情况,而是期望实现能够维护基本原理中描述的 C 精神,包括“不要阻止程序员做需要做的事情”的原则。要做”。这通常会导致质量实现在需要满足客户需求时优先考虑行为的定义,而在允许也满足客户需求的优化时优先考虑行为的“未定义”。
将 C 或 C++ 标准视为定义有用语言的唯一方法是识别一类操作,其行为由标准的一部分描述并由另一部分分类为 UB,并将该类别中的操作视为实施质量问题不在本标准的管辖范围内。该标准的作者希望编译器编写者对其客户的需求保持敏感,因此不会将行为定义和未定义之间的冲突视为一个特定问题。因此,他们认为没有必要以可以一致应用而不会产生此类冲突的方式定义诸如“对象”、“左值”、“生命周期”和“访问”等术语,因此他们创建的定义不能用于决定的目的当存在此类冲突时是否应定义特定的操作。
因此,除非或直到标准认识到与对象和访问它们的方式相关的更多概念,否则旨在适合某些目的的质量实现是否应该支持特定操作的问题将取决于其作者是否应该被期望认识到该行动对于此目的是有用的。
| 归档时间: |
|
| 查看次数: |
180 次 |
| 最近记录: |