为何简单场景下返回不可拷贝不可移动右值时拷贝消除不生效?
为何简单场景下拷贝消除对具名右值无效?C++标准是否存在技术障碍?
我搞不懂为什么在简单场景下,拷贝消除(copy elision)对具名右值不起作用?C++标准里加入这个特性有技术障碍吗?这个问题曾在《How does guaranteed copy elision work?》线程中被部分讨论,但没得到完整解答。
符合预期运行的示例
struct x { x(int) {} void bar() {} x(x&&) = delete; x& operator=(x&&) = delete; }; x foo1() { return x{1}; // 正常编译运行 }
我理解调用foo1时,编译器会通过类似this的隐式参数传递变量构造的存储地址,这也是拷贝消除生效时返回值无额外开销的原因。
无法生效的场景
但如果是在函数中间构造对象再返回,这个机制就失效了:
x foo2() { auto r = x{1}; r.bar(); return r; // 编译报错 }
编译时的错误信息:
g++ -c --std=c++2a 1.cpp 1.cpp: In function ‘x foo2()’: 1.cpp:19:10: error: use of deleted function ‘x::x(x&&)’ 19 | return r; | ^ 1.cpp:6:9: note: declared here 6 | x(x&&) = delete; | ^
在我看来,这种单个变量的场景下,编译器完全可以像foo1那样,在指定存储位置构造x对象并返回。而且如果启用x的移动构造函数,调用foo2时编译器并不会调用它——这就是具名返回值优化(NRVO),但为什么删除移动构造后就不行了?
明确理解的无效场景
以下场景无法生效的原因我完全清楚:
x foo3() { auto r1 = x{1}; auto r2 = x{2}; return condition ? r1 : r2; }
这段代码创建了两个对象,返回其中一个必须使用move操作,这个逻辑没问题。但单个变量的情况我始终无法理解。
可行的临时解决方案
顺带一提,有个在部分场景下可行的 workaround:
struct y: x { y(int i) : x(i) { bar(); } }; y foo3() { return y{1}; // 不直接返回x,而是返回派生类y,所有操作都在y的构造函数中完成 }
内容的提问来源于stack exchange,提问作者Dmytro Ovdiienko
相关产品推荐
相关产品推荐

