Jos*_*eld 5

使用无效对象(引用、指针等)未定义行为的原因是左值到右值的转换(\xc2\xa74.1):

\n\n
\n

如果泛左值引用的对象不是 T 类型的对象,也不是从 T 派生的类型的对象,或者该对象未初始化,则需要此转换的程序将具有未定义的行为。

\n
\n\n

假设我们没有重载operator&,一元&运算符采用左值作为其操作数,因此不会发生转换。只有一个标识符,就像 in 一样,x;也不需要转换。仅当引用用作期望操作数为右值的表达式中的操作数时,才会出现未定义的行为 - 大多数运算符都是这种情况。关键是,这样做&x实际上并不需要访问 的值x。左值到右值的转换发生在那些需要访问其值的运算符上。

\n\n

我相信您的代码定义良好。

\n\n

operator&重载后,表达式将&x转换为函数调用,并且不遵守内置运算符的规则 - 相反,它遵循函数调用的规则。对于&x,函数调用的转换结果为x.operator&()operator&(x)。在第一种情况下,x当使用类成员访问运算符时,将发生左值到右值的转换。在第二种情况下, 的参数operator&将被复制初始化x(如T arg = x),并且其行为取决于参数的类型。例如,在参数是左值引用的情况下,不存在未定义的行为,因为不会发生左值到右值的转换。

\n\n

因此,如果operator&的类型被重载x,则代码可能定义良好,也可能不明确,具体取决于函数的调用operator&

\n\n

您可能会争辩说,一元运算&符依赖于至少存在一些您拥有地址的有效存储区域:

\n\n
\n

否则,如果表达式的类型为T,则结果的类型为“指向T”的指针,并且是纯右值,即指定对象的地址

\n
\n\n

对象被定义为存储区域。所引用的对象被销毁后,该存储区域将不再存在。

\n\n

我更愿意相信,如果实际访问了无效对象,它只会导致未定义的行为。该引用仍然相信它引用了某个对象,并且即使它不存在,它也可以愉快地给出它的地址。然而,这似乎是该标准的一个不明确的部分。

\n\n
\n\n

在旁边

\n\n

作为未定义行为的示例,请考虑x + x。现在我们遇到了标准中另一个未指定的部分。+未指定操作数的值类别。通常从 \xc2\xa75/8 推断,如果未指定,则需要一个纯右值:

\n\n
\n

每当左值表达式作为需要该操作数纯右值的运算符的操作数出现时,左值到右值 (4.1)、数组到指针 (4.2) 或函数到指针 (4.3) 标准转换为用于将表达式转换为纯右值。

\n
\n\n

现在因为x是一个左值,所以需要左值到右值的转换,并且我们会得到未定义的行为。这是有道理的,因为加法需要访问 的值x才能算出结果。

\n

  • 但是,& 运算符可以重载。 (2认同)
  • 左值是引用对象的表达式,“&”的结果是对象的地址。但在这种情况下,没有对象。我不太确定标准到底说了什么。 (2认同)

Rei*_*ica 5

首先,非常有趣的问题。

我会说这是未定义的行为,假设“悬空引用”意味着“引用对象的生命周期已经结束并且对象占用的存储被重用或释放”。我的推理基于以下标准裁决:

3.8 §3:

本国际标准中赋予对象的属性仅适用于给定对象在其生命周期内。[注意:特别是,在对象的生命周期开始之前和生命周期结束之后,对象的使用有很大的限制,如下所述...]

所有“如下所述”的情况均指

在对象的生命周期开始之前但在对象将占用的存储空间已经分配之后38 或者,在对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前

1.3.24: 未定义的行为

本国际标准未对其施加要求的行为 [注意:当本国际标准省略任何明确的行为定义或程序使用错误的构造或错误的数据时,可能会出现未定义的行为。...]

我将以下思路应用于上述引文:

  1. 如果标准没有描述某种情况的行为,则行为是未定义的。
  2. 该标准仅描述对象在其生命周期内的行为,以及在其生命周期开始/结束附近的一些特殊情况。这些都不适用于我们的悬空参考。
  3. 因此,以任何方式使用 danling 引用都没有标准规定的行为,因此行为是未定义的。