提问人:Luchian Grigore 提问时间:2/6/2013 更新时间:2/6/2013 访问量:1352
悬空引用和未定义的行为
Dangling references and undefined behavior
答:
使用无效对象(引用、指针等)未定义行为的原因在于左值到右值的转换 (§4.1):
如果 glvalue 引用的对象不是 T 类型的对象,也不是派生自 T 的类型的对象,或者如果该对象未初始化,则需要此转换的程序具有未定义的行为。
假设我们没有重载,一元运算符将左值作为其操作数,因此不会发生转换。只有一个标识符,就像 中一样,也不需要转换。只有当引用在期望该操作数为右值的表达式中用作操作数时,才会获得未定义的行为 - 大多数运算符都是这种情况。关键是,执行实际上并不需要访问 的值。左值到右值的转换发生在那些需要访问其值的运算符上。operator&
&
x;
&x
x
我相信你的代码定义得很好。
当重载时,表达式将转换为函数调用,并且不遵守内置运算符的规则,而是遵循函数调用的规则。对于 ,函数调用的转换结果为 或 。在第一种情况下,当使用类成员访问运算符时,将发生左值到右值的转换。在第二种情况下,将 的参数用 (如 中 ) 进行复制初始化,其行为取决于参数的类型。例如,如果参数是左值引用,则不存在未定义的行为,因为不会发生左值到右值的转换。operator&
&x
&x
x.operator&()
operator&(x)
x
operator&
x
T arg = x
因此,如果 的类型重载,则代码可能定义良好,也可能定义不明确,具体取决于函数的调用。operator&
x
operator&
你可能会争辩说,一元运算符依赖于至少存在一些有效的存储区域,而你拥有的地址是:&
否则,如果表达式的类型为 ,则结果的类型为“指针”,并且是作为指定对象地址的 prvalue
T
T
对象被定义为存储区域。在引用的对象被销毁后,该存储区域将不再存在。
我更愿意相信,只有当实际访问无效对象时,它才会导致未定义的行为。引用仍然认为它指的是某个对象,即使它不存在,它也可以愉快地给出它的地址。然而,这似乎是标准中一个未明确说明的部分。
旁白
作为未定义行为的示例,请考虑 。现在,我们遇到了标准中另一个未指定的部分。未指定操作数的值类别。通常从 §5/8 中推断,如果未指定,则它需要 prvalue:x + x
+
每当 glvalue 表达式显示为需要该操作数的 prvalue 的运算符的操作数时,将应用左值到右值 (4.1)、数组到指针 (4.2) 或函数到指针 (4.3) 标准转换以将表达式转换为 prvalue。
现在因为是左值,所以需要左值到右值的转换,我们得到未定义的行为。这是有道理的,因为加法需要访问 的值,以便它可以计算出结果。x
x
评论
&
std::vector
假设使用有效对象进行初始化,然后销毁该对象,则 §3.8/6 适用:x
同样,在对象的生存期开始之前,但在分配了该对象将占用的存储之后,或者在对象的生存期结束后,在重用或释放对象占用的存储之前,可以使用引用原始对象的任何 glvalue,但只能以有限的方式使用。对于正在建造或破坏的物体,见12.7。否则,此类 glvalue 是指已分配的存储 (3.7.4.2),并且使用不依赖于其值的 glvalue 属性是明确定义的。如果出现以下情况,程序具有未定义的行为:
— 左值到右值的转换 (4.1) 应用于这样的 glvalue,
— glvalue 用于访问非静态数据成员或调用 对象,或者
— GLvalue 绑定到对虚拟基类 (8.5.3) 的引用,或者
— glvalue 用作 dynamic_cast (5.2.7) 的操作数或 typeid 的操作数。
因此,简单地获取地址是明确定义的,并且(参考相邻的段落)甚至可以有效地用于创建一个新对象来代替旧对象。
至于不取地址而只是写,那真的完全没有作用,它是 的适当子表达。所以也没关系。x
&x
评论
首先,非常有趣的问题。
我会说这是未定义的行为,假设“悬空引用”意味着“引用对象的生命周期已结束,并且对象占用的存储已被重用或释放”。我的推理基于以下标准裁决:
3.8 §3:
本国际标准中归因于对象的属性仅适用于给定对象 在其一生中。[ 注意:特别是,在对象的生存期开始之前和结束之后 对象的使用有很大的限制,如下所述......]
所有“如下所述”的情况均指
在对象的生存期开始之前,但在对象将占用的存储之后 已分配38 或者,在对象的生存期结束后,在对象占用的存储之前 重复使用或发布
1.3.24: 未定义的行为
本国际标准不设要求的行为 [ 注意:当本国际标准省略任何明确的定义时,可能会出现未定义的行为 行为或程序使用错误构造或错误数据时。...]
我对上述引文应用以下思路:
- 如果标准没有描述某种情况的行为,则行为是未定义的。
- 该标准仅描述了对象在其生命周期内的行为,以及接近其生命周期开始/结束的一些特殊情况。这些都不适用于我们的悬空参考。
- 因此,以任何方式使用丹灵引用都不具有标准规定的行为,因此该行为是未定义的。
评论
*x;
x
是一个参考,所以......不是很合法。*x