我们可以访问一个不存在的联盟的成员吗
Can we access a member of a non-existing union?
在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?
}
注意:Bellow解释了我在标准中没有掌握的内容,以及为什么上面的例子可能有用
我想知道在什么情况下[basic.lval]/11.6会有用。
[class.mfct.non-static]/2确实禁止我们调用"casted to"并集或聚合的成员函数:
如果为不是类型X或从X派生的类型的对象调用类X的非静态成员函数,则行为是未定义的。
考虑到静态数据成员访问或静态成员功能可以使用限定名(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的相应成员被提名了一样。
和[class.union]/1:
在并集中,如果的名称引用了其生存期已开始但尚未结束的对象,则非静态数据成员处于活动状态。
Q3:表达式"其名称引用"是否意味着"对象"实际上是在一个活的联盟中构建的对象?或者它可能是因为[basic.lval]/11.6而引用对象a
。
[expr.ref]/4.2定义了如果E2
是非静态数据成员,E1.E2
的含义:
如果
E2
是非静态数据成员[…],则表达式指定由第一个表达式。
这只为第一个表达式实际指定对象的情况定义行为。由于在您的示例中,第一个表达式没有指定对象,因此该行为是通过省略来定义的;参见[defns.definned]("当本文档省略了任何明确的行为定义时,可能会出现未定义的行为…")。
您也误解了"访问"在严格别名规则中的含义。它的意思是"读取或修改对象的值"([defns.access])。命名非静态数据成员的类成员访问表达式既不读取也不修改任何对象的值,因此不是"访问",因此,由于类成员访问表达,永远不会有"访问…通过"聚合或联合类型"的glvalue。
[basic.lval]/11.6本质上是从C复制的,在C中它实际上意味着什么,因为分配或复制struct
或union
会访问整个对象。在C++中这是没有意义的,因为类类型的赋值和复制是通过特殊的成员函数执行的,这些成员函数要么执行成员复制(因此单独"访问"成员),要么对对象表示进行操作。见核心问题2051。
有很多情况,特别是涉及类型双关和并集的情况,其中C或C++标准的一部分描述了某个操作的行为,另一部分将重叠的操作类描述为调用UB,并且重叠区域包括应当由所有实现一致地处理的一些动作,以及在至少一些实现上支持不切实际的其他动作。该标准的作者并没有试图完全描述所有应按定义处理的情况,而是希望实施将寻求维护基本原理中描述的C的精神,包括"不要阻止程序员做需要做的事情"的原则。这通常会导致高质量的实施在必要时优先考虑行为的定义,以满足客户的需求,而在允许同时满足客户需求的优化时优先考虑"不确定性"行为。
将C或C++标准视为定义有用语言的唯一方法是识别一类行为由标准的一部分描述并由另一部分分类为UB的行为,并将该类行为的处理视为标准管辖范围之外的实施质量问题。该标准的作者希望编译器编写者对客户的需求敏感,因此没有将行为定义和不确定性之间的冲突视为一个特殊的问题。因此,他们认为没有必要定义"对象"、"左值"、"寿命"answers"访问"等术语,这些术语可以在不产生此类冲突的情况下一致应用,因此,他们创建的定义无法用于决定在存在此类冲突时是否应定义特定行动。
因此,除非或直到标准认识到更多与对象相关的概念和访问它们的方式,否则是否应该期望适合某一目的的高质量实施来支持某一行动的问题将取决于其作者是否应该认识到该行动对该目的有用。
- 我们可以访问一个不存在的联盟的成员吗
- C++:对不存在的命名空间使用命名空间指令
- g++ 说函数不存在,即使包含正确的标头
- 是否存在一个范围::视图::group_by对应项,它将所有元素都考虑在内,而不是只考虑连续的元素
- 如果路径的每个元素不存在,请为其创建一个目录
- C++:你如何确定一个解是微不足道的还是不存在的二次函数?
- 为什么要对 map::find 应用不存在的键将返回一个C++中第一个值映射大小的迭代器
- 如何在Visual C++中有条件地包含一个不存在的文件
- 在QT中制作了一个文本浏览器,但代码就像它不存在一样?
- 如果这些文件中的任何一个不存在,则执行目标
- 我想在 c++ 中为 map<> 的给定键返回一个值。如果 KEY 在 map<> 中不存在,要返回什么?
- 有一个指向std::vector-element的指针,该指针目前还不存在,但稍后将被构造,这是个好主意吗?
- 如何在一个套接字上实现并行请求和响应的非阻塞客户端-服务器通信模型,而不存在数据竞争
- [C++]在此代码之前不存在的析构函数中引发的内存错误。尝试创建一个新的动态数组并填充它
- 是否存在一个c++函数(内置或其他),可以在不重复操作的情况下给出整数除法和模除法的结果
- Winsock2 的 listen() 函数为每个端口查找一个连接;甚至那些不存在的?
- 对象寿命相关;下面的问题不存在一个术语/模式什么的吗
- 我收到一个未定义的引用错误,指向不存在的东西
- 实现一个 dinamic 数组(我可以访问不存在的位置)
- 创建一个新文件(如果不存在)