【发布时间】:2019-04-08 14:45:32
【问题描述】:
在 c++ 标准中,[basic.lval]/11.6 中说:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义:[...]
- 一种聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一,[...]
这句话是严格别名规则的一部分。
它可以让我们访问不存在的工会的非活动成员吗?如:
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?
}
注意:下面解释一下我在标准中没有掌握的内容,以及为什么上面的示例可能有用。
我想知道 [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”联合的成员。我考虑过使用最后一条标准规则来实现“优化变体”。该变体可以包含 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();
}
}
};
对AB_variant::foo 的调用不会调用未定义的行为,只要它的参数引用AB_variant 类型的对象,这要归功于指针互转换 规则[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));
这里的问题是我试图访问一个不存在的联合的非活动成员。
两个相关的标准段落是[class.mem]/25:
在具有结构类型 T1 的活动成员的标准布局联合中,允许读取结构类型 T2 的另一个联合成员的非静态数据成员 m,前提是 m 是 T1 的公共初始序列的一部分,并且T2;就好像T1的相应成员被提名了一样。
在联合中,如果其名称引用的对象其生命周期已开始但尚未结束,则非静态数据成员处于活动状态。
Q3:“它的名字所指”这个表达是否意味着“一个对象”实际上是一个构建在一个活的联合体中的对象?还是因为 [basic.lval]/11.6 而引用对象 a。
【问题讨论】:
-
Q1, Q2... Q3... 这不是符合 SO 的“过于宽泛”的定义吗?
-
@StoryTeller 我认为它们只是问题的结构。
-
不幸的是,这可能更像是一个讨论问题,而不是一个可能有特定答案的问题,所以我倾向于同意:“太宽泛了”。
-
第一个示例似乎是明显的严格别名违规,因为您取消引用指向
A的指针实际上是指向int的指针。 (实际上它看起来像一个错字,应该是reinterpret_cast<A*>(&j),甚至不能按原样编译) -
那么请将您的问题限制在与标题直接相关的内容上。其他一切都只是臃肿。
标签: c++ language-lawyer unions strict-aliasing class-members