【问题标题】:What happened to the "aggregate or union type that includes one of the aforementioned types" strict aliasing rule?“包含上述类型之一的聚合或联合类型”严格别名规则发生了什么?
【发布时间】:2019-07-03 22:12:18
【问题描述】:

以前,在 basic.lval 中,有这个要点:

在其元素或非静态数据成员(递归地包括子聚合或包含的联合的元素或非静态数据成员)中包含上述类型之一的聚合或联合类型,

在当前的draft,已经没有了。

WG21 网站上有一些背景资料:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1359r0.html#2051:

7.2.1 [basic.lval] 第 10 段的别名规则改编自 C 并添加了 C++。然而,一些点要么不适用,要么被其他点所包含。例如,在 C 中需要为结构赋值提供聚合和联合类型,在 C++ 中,这是通过 C++ 中的构造函数和赋值运算符完成的,而不是通过访问完整的对象。

谁能给我解释一下,这是什么意思?这个严格的别名规则与 C 中的结构赋值有什么关系?

cppreference 谈到这条规则:

这些项目符号描述了 C++ 中不能出现的情况

我不明白,为什么这是真的。例如,

struct Foo {
    float x;
};

float y;
float z = reinterpret_cast<Foo*>(&y)->x;

最后一行似乎与项目符号点描述的一样。它通过一个聚合访问y(一个float),其中包括一个float(成员x)。

有人能解释一下吗?

【问题讨论】:

  • 第一个要点无论如何都没有多大意义(在任何一种语言中),所以我很高兴看到它消失了!
  • @M.M 你能解释一下“使用任何一种语言”的说法吗?
  • @LanguageLawyer 在 C 或 C++ 中
  • @M.M 我的意思是为什么它在 C 中没有意义
  • @LanguageLawyer 说你有 struct S { int a; float b; };float *c = malloc(100); 并且 int 和 float 大小相同且没有填充;那么根据该文本,c[0] = c[1] = 0; S s = *(S *)c; 不是别名违规,因为 float c[0] 由具有 float 作为其成员的聚合类型访问(不要介意成员的偏移量没有的事实) t对应正在访问的内存)

标签: c++ struct language-lawyer strict-aliasing


【解决方案1】:

用于访问y 的存储值的左值不是*reinterpret_cast&lt;Foo*&gt;(&amp;y),类型为Foo,而是reinterpret_cast&lt;Foo*&gt;(&amp;y)-&gt;x,类型为float。使用float 类型的左值访问float 很好。在 C++ 中,您不能“访问联合或结构的值”(作为整体),您只能访问单个成员。您引用的基本原理指出了 ​​C 和 C++ 之间的区别:

  struct X { int a, b; };
  struct X v1 = {1, 2}, v2;
  v2 = v1;

在 C 中,标准规定赋值会加载 v1(作为整体)的值以将其分配给 v2。这里v1.av2.b 对象的值(都具有int 类型)使用struct X 类型的左值(不是 int)访问。

在 C++ 中,标准规定赋值调用编译器生成的赋值运算符,相当于

struct X {
   ...
   struct X& operator=(const struct X&other)
   {
       a = other.a;
       b = other.b;
   }
};

在这种情况下,调用赋值运算符不会访问任何值,因为 RHS 是通过引用传递的。并且执行赋值运算符会分别访问两个 int 字段(这很好,即使没有聚合规则),所以这又不是通过 struct X 类型的左值访问值。

【讨论】:

  • 如果从malloc 接收的存储是使用一个 PODS 写入并使用另一个具有相同布局的 PODS 读取的,该规则会说什么?
  • 我同意这个答案,但我认为问题中的一个不成文的假设是A.B“访问”A(因此P-&gt;B“访问”*P)。我在标准中没有看到任何明确指出并非如此的内容,但A.B 本身并不意味着任何东西的修改或左值到右值都支持它。
  • @supercat 这个规则还是可以的。如果这两种类型是布局兼容的,则各个成员是布局兼容的。如果它们是布局兼容的,则通过指向不同 POD 类型的指针从一种 POD 类型访问成员由“别名子句”中的早期项目符号介绍。
  • @MichaelKarcher:据我所知,gcc 和 clang 都不支持这种用法。使 C 规则起作用所需要的是认识到通过明显从左值派生的指针的访问应该被视为对该左值的访问,但 gcc 也不支持这种用法。
  • 如果Foostruct Foo { char c; float x; }; 怎么办?我们不能说在float z = reinterpret_cast&lt;Foo*&gt;(&amp;y)-&gt;x; 中我们通过float 类型的左值访问y,所以这也可以吗?
猜你喜欢
  • 2019-11-14
  • 1970-01-01
  • 2019-01-29
  • 2013-10-01
  • 2020-08-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-19
  • 1970-01-01
相关资源
最近更新 更多