You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何删除拷贝/移动构造函数时C++命名返回值优化编译失败?

C++返回值优化(RVO/NRVO)与删除拷贝/移动构造函数的编译问题解析

问题场景

在gcc 13.1环境下,测试C++11/17/20/23标准代码时发现:

  • 当结构体Foo删除拷贝或移动构造函数后,返回命名局部对象的makeFoo()无法编译;若不删除构造函数,命名返回值优化(NRVO)会生效,全程无拷贝/移动操作。
  • 直接返回prvalue(临时对象)的makeFoo()不受构造函数删除的影响,普通返回值优化(RVO)正常工作,代码可编译运行。

测试代码示例

#include <memory>
#include <iostream>

struct Foo{
    Foo(int v): a{v} { std::cout << "Create!\n"; }
    ~Foo() { std::cout << "Destruct!\n"; }

    Foo(const Foo&)=delete;
    Foo(Foo&&)=delete;
    
    int a;
};

// 无法编译!
Foo makeFoo() {
    Foo foo{5};
    return foo;
}

// 可正常编译!
//Foo makeFoo() {
//    return Foo{5};
//}

int main() {
    auto foo = makeFoo();
    std::cout << "Hello world! " << foo.a << "\n";
}

现象解释

核心差异在于C++标准对两种返回值优化的「合法性前提」要求不同:

1. 命名返回值优化(NRVO):优化可选,需保证未优化路径合法

NRVO针对返回函数内命名局部对象的场景(比如代码中的foo)。虽然主流编译器(如gcc)几乎都会触发NRVO,直接在调用方的内存空间构造对象,跳过拷贝/移动,但C++标准明确规定:

NRVO是编译器的可选优化,无论是否执行优化,代码必须保证「如果不做优化时也能合法编译」。

当你写return foo;时,编译器首先要检查:如果不做NRVO,是否能通过拷贝或移动构造函数将foo转移到返回值位置?由于Foo删除了这两个构造函数,这条「未优化路径」是非法的,因此编译器直接报错,不会考虑是否执行优化。

反之,若不删除构造函数,编译器会触发NRVO,实际运行时不会调用拷贝/移动构造函数——但这只是编译器的优化行为,并非标准强制要求。

2. 普通返回值优化(RVO):C++17及以后为强制语义,无需依赖拷贝/移动构造函数

针对直接返回prvalue(比如return Foo{5};这种临时对象)的场景,C++17对语言语义做了重大修改:

此类场景下的RVO不再是可选优化,而是语言的强制语义——编译器必须直接在调用方的目标变量位置构造临时对象,完全跳过拷贝/移动步骤。

这意味着从C++17开始,return Foo{5};等价于直接在main()的foo变量位置构造对象,根本不会触发拷贝/移动构造函数的调用,自然也不需要这些构造函数存在。哪怕删除了拷贝/移动构造函数,代码依然合法。

在C11/14标准中,RVO仍是可选优化,但gcc等主流编译器会主动执行优化,因此实际测试中代码也能编译通过——不过严格来说,C11/14标准并未强制要求这种情况下可以跳过构造函数检查,属于编译器的宽松实现。

内容的提问来源于stack exchange,提问作者Nathan Doromal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 15:13:21