C++ &运算符重载:取函数返回右值地址合法还是未定义行为?
重载取地址运算符后获取函数返回右值的地址是否合法?
先看问题中的代码:
#include <iostream> struct Foo { int val; Foo* operator &() { // 与普通"&"作用一致 return this; } }; Foo test() { return Foo{3}; } int main() { Foo* p = &test(); // 为何允许且可运行? printf("%d", p->val); }
核心疑问:通常函数返回值是右值,无法直接取地址,但重载operator&()后代码能编译运行,这属于合法C++语法还是未定义行为?
编译通过的原因:合法的语法扩展
内置的取地址运算符&确实不能作用于纯右值(prvalue),因为纯右值没有实际的内存地址。但当类重载了operator&()成员函数后,&test()的语义发生了变化:
- 此时
&test()不再调用内置运算符,而是调用Foo类的重载成员函数operator&()。 - C标准允许右值对象调用无引用限定符的成员函数(这里
Foo::operator&()没有&或&&限定),编译器会将test()返回的临时Foo对象作为this指针的指向,调用operator&()并返回this指针,这一步完全符合C语法规范。
运行结果的本质:存在未定义行为
虽然代码能编译运行,且在Clang-14、GCC-12中能得到预期结果,但后续通过指针访问临时对象的操作属于未定义行为:
test()返回的临时Foo对象是纯右值,其生命周期仅持续到创建它的完整表达式结束——也就是Foo* p = &test();这条语句执行完毕后,临时对象就会被销毁,此时p成为悬垂指针。- 你提供的汇编显示编译器将临时对象存储在
main函数的栈帧([rbp-16])中,printf执行时该栈空间尚未被覆盖,所以能输出正确值,但这只是编译器的具体实现细节,C++标准不保证临时对象的内存会保留原值,也不保证后续访问不会触发崩溃、输出错误值等问题。
附上你提供的Clang 14汇编输出:
[Clang 14.0.0 无编译参数] main: # @main push rbp mov rbp, rsp sub rsp, 16 call test() mov dword ptr [rbp - 16], eax <--- lea rdi, [rbp - 16] <--- call Foo::operator&() mov qword ptr [rbp - 8], rax mov rax, qword ptr [rbp - 8] mov esi, dword ptr [rax] movabs rdi, offset .L.str mov al, 0 call printf xor eax, eax add rsp, 16 pop rbp ret
内容的提问来源于stack exchange,提问作者StefTheDrummer
相关产品推荐
相关产品推荐

