使用无效对象(引用、指针等)未定义行为的原因是左值到右值的转换(第 4.1 节):
如果泛左值所引用的对象不是 T 类型的对象,也不是从 T 派生的类型的对象,或者如果该对象未初始化,则需要此转换的程序具有未定义的行为。
假设我们没有重载operator&,一元& 运算符将左值作为其操作数,因此不会发生转换。只有一个标识符,如x; 也不需要转换。只有当引用被用作表达式中的操作数时,您才会得到未定义的行为,该表达式期望该操作数是右值 - 大多数运算符都是这种情况。关键是,执行&x 实际上并不需要访问x 的值。左值到右值的转换发生在那些需要访问其值的运算符中。
我相信您的代码定义明确。
当operator& 被重载时,表达式&x 被转换为函数调用,并且不遵守内置运算符的规则——而是遵循函数调用的规则。对于&x,转换为函数调用会产生x.operator&() 或operator&(x)。在第一种情况下,当使用类成员访问运算符时,x 将发生左值到右值的转换。在第二种情况下,operator& 的参数将被复制初始化为x(如T arg = x),其行为取决于参数的类型。例如,在参数是左值引用的情况下,没有未定义的行为,因为不会发生左值到右值的转换。
因此,如果 operator& 针对 x 的类型重载,则代码可能定义明确,也可能不明确,具体取决于 operator& 函数的调用。
您可能会争辩说,一元 & 运算符依赖于至少有一些您拥有地址的有效存储区域:
否则,如果表达式的类型为T,则结果类型为“指向T”的指针,并且是prvalue,即指定对象的地址
对象被定义为一个存储区域。在所引用的对象被销毁后,该存储区域不再存在。
我更愿意相信,如果实际访问了无效对象,它只会导致未定义的行为。引用仍然认为它指的是某个对象,即使它不存在,它也可以愉快地给出它的地址。但是,这似乎是标准中没有明确说明的部分。
一边
作为未定义行为的示例,请考虑x + x。现在我们遇到了标准中另一个指定不明确的部分。未指定+ 的操作数的值类别。一般从 §5/8 推断,如果没有指定,那么它期望一个纯右值:
当一个泛左值表达式作为一个操作数的操作数出现时,该操作数需要一个纯右值,左值到右值 (4.1)、数组到指针 (4.2) 或函数到指针 (4.3)应用标准转换将表达式转换为纯右值。
现在因为x 是一个左值,所以需要左值到右值的转换,我们会得到未定义的行为。这是有道理的,因为加法需要访问x 的值,以便计算出结果。