挂起的引用和未定义的行为
Dangling references and undefined behavior
假设一个悬空引用x
。只写是未定义的行为吗
&x;
甚至
x;
假设x
是用有效对象初始化的,然后被销毁,§3.8/6适用:
类似地,在对象的生存期开始之前,但在对象将占用的存储已被分配之后,或者在对象的生命期结束之后,在对象占用的存储被重用或释放之前,可以使用任何引用原始对象的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:
本国际标准中物体的特性仅适用于给定物体在其使用寿命期间。[注意:特别是在对象的生存期开始之前和结束之后如下文所述,对对象的使用有重大限制…]
"如下所述"的所有情况均指
在对象的生存期开始之前,但在对象将占用的存储空间结束之后allocated38,或者,在对象的生存期结束后,在对象占用的存储之前重复使用或释放
1.3.24:未定义的行为
本国际标准未要求的行为【注:当本国际标准省略了对行为或程序使用错误构造或错误数据时……]
我将以下思路应用于上述报价:
- 如果标准没有描述一种情况下的行为,那么行为是不明确的
- 该标准只描述了对象在其生命周期内的行为,以及在其生命期开始/结束时的一些特殊情况。这些都不适用于我们的悬空引用
- 因此,以任何方式使用danling参考都没有标准规定的行为,因此行为是未定义的
使用无效对象(引用、指针等)的未定义行为是左值到右值的转换(§4.1):
如果glvalue所引用的对象不是T类型的对象,也不是从T派生的对象,或者如果该对象未初始化,则需要进行此转换的程序具有未定义的行为。
假设我们没有重载operator&
,一元&
运算符将左值作为其操作数,因此不会发生转换。只有一个标识符(如x;
)也不需要转换。只有当引用被用作表达式中的操作数时,您才会得到未定义的行为,该表达式期望该操作数是右值——大多数运算符都是这样。关键是,执行&x
实际上并不需要访问x
的值。Lvalue到右值的转换发生在那些需要访问其值的运算符中。
我相信您的代码定义良好。
当operator&
被重载时,表达式&x
被转换为函数调用,并且不遵守内置运算符的规则,而是遵循函数调用的规则。对于&x
,转换为函数调用会产生x.operator&()
或operator&(x)
。在第一种情况下,当使用类成员访问运算符时,将在x
上进行左值到右值的转换。在第二种情况下,operator&
的自变量将使用x
进行复制初始化(如在T arg = x
中),其行为取决于自变量的类型。例如,在参数是左值引用的情况下,不存在未定义的行为,因为不会发生左值到右值的转换。
因此,如果operator&
对于x
的类型被重载,则根据对operator&
函数的调用,代码可能是定义良好的,也可能不是定义良好的。
你可能会争辩说,一元&
运算符依赖于至少有一些有效的存储区域,你有地址:
否则,如果表达式的类型为
T
,则结果的类型为"指向T
的指针",并且是指定对象的地址的prvalue
一个对象被定义为一个存储区域。在所引用的对象被销毁后,该存储区域将不复存在。
我更愿意相信,只有当无效对象被实际访问时,它才会导致未定义的行为。引用仍然认为它指的是某个对象,即使它不存在,它也可以很高兴地给出它的地址。然而,这似乎是标准中一个不明确的部分。
旁白
作为未定义行为的示例,请考虑x + x
。现在,我们达到了标准中另一个不明确的部分。未指定+
的操作数的值类别。通常从§5/8中推断,如果未指定,则预期prvalue:
每当glvalue表达式作为期望该操作数为prvalue的运算符的操作数出现时,都会应用左值到右值(4.1)、数组到指针(4.2)或函数到指针(4.3)标准转换将表达式转换为prvalue。
现在因为x
是一个左值,所以需要进行左值到右值的转换,我们得到了未定义的行为。这是有道理的,因为加法需要访问x
的值,这样它才能计算出结果。
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- 使用mysql c++连接器的未定义引用
- 对 Scalar ::Scalar() 的未定义引用
- 对复制 CTOR 和 CTOR 的未定义引用
- 对显式实例化的模板函数的未定义引用
- TensorRT (C++ API) 对"createNvOnnxParser_INTERNAL"的未定义引用
- 2个模板化类的非模板友元函数未定义引用错误
- 编译 libfluid 样本控制器时对"event_base_del_virtual"的未定义引用
- 获取对function_name的未定义引用
- 对 'std::thread::_M_start_thread CMake 的未定义引用进行基准测试
- 对结构方法的未定义引用
- 使用内联函数 c++ 的未定义引用
- 对 CMake 中'cudaRegisterLinkedBinary'链接错误的未定义引用?
- 对 DLOPEN 的未定义引用
- QT C++中对全局变量的未定义引用
- 快速数学导致对"__pow_finite"的未定义引用
- 对 boost::system::d etail::system_category_instance 的未定义引用,从
- OpenCV 3.4.3 中对 'cv::String::d eallocate()' 错误的未定义引用