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

为何命名返回值优化(NRVO)在此场景下不生效?

关于C++ NRVO、拷贝/移动构造函数的问题

我删除了GFXAPIImage类的拷贝构造函数,原本预期会触发命名返回值优化(NRVO)直接初始化对象,但编译报错。想请教两个问题:

  1. 是否必须声明拷贝构造函数?
  2. 如果必须声明,类成员UniqueBufferPointer无法拷贝的问题该如何解决?

另外补充:如果给GFXAPIImage声明移动构造函数就能正常编译,这是不是说明编译器用了移动构造?但返回的是左值f,为什么会触发移动?

复现代码

class UniqueBufferPointer
{
public:
    UniqueBufferPointer() {}
    UniqueBufferPointer(const UniqueBufferPointer& other) = delete;
    UniqueBufferPointer(UniqueBufferPointer&& other) {}
    UniqueBufferPointer& operator=(const UniqueBufferPointer&) = delete;
};

struct GFXAPIImage
{
    GFXAPIImage() {}

    UniqueBufferPointer handle;
    GFXAPIImage(const GFXAPIImage&) = delete;
    //GFXAPIImage(const GFXAPIImage& other) = default; // 若使用此语句,编译器会报错无法访问UniqueBufferPointer的拷贝构造函数
};

GFXAPIImage func()
{
    GFXAPIImage f;
    return f; //报错:GFXAPIImage(const GFXAPIImage&)无法被引用,它是已删除的函数
}

int main()
{
    GFXAPIImage f = func();
}

编译环境:Visual Studio,编译参数/O2


问题解答

核心原因

NRVO是编译器的可选优化,而非C++语言强制要求的行为。标准规定,无论编译器是否做NRVO,代码必须保证在不优化的场景下也能合法编译——这意味着类必须拥有可访问的拷贝构造或移动构造函数,作为优化失效时的后备路径。

你的代码中,GFXAPIImage显式删除了拷贝构造函数,同时因为显式声明(删除)了拷贝构造,编译器不会为它生成默认的移动构造函数(C++11及以后规则:若用户声明/删除了拷贝构造、拷贝赋值、析构中的任意一个,默认移动构造/赋值不会自动生成)。因此编译器找不到可用的拷贝或移动构造,直接报错。

具体问题解答

  1. 是否必须声明拷贝构造函数?
    不需要。但必须保证类拥有可访问的拷贝构造或移动构造函数二者其一即可。NRVO不是强制的,编译器可能在某些场景下不触发优化(比如关闭优化开关),此时需要依赖拷贝或移动完成返回操作。

  2. 成员UniqueBufferPointer无法拷贝的解决办法
    UniqueBufferPointer是独占语义类型(拷贝被删除,仅支持移动),因此GFXAPIImage需要适配这种语义:显式声明并定义移动构造函数,调用成员的移动构造来转移资源:

struct GFXAPIImage
{
    GFXAPIImage() {}
    UniqueBufferPointer handle;
    GFXAPIImage(const GFXAPIImage&) = delete;
    // 显式定义移动构造
    GFXAPIImage(GFXAPIImage&& other) noexcept : handle(std::move(other.handle)) {}
};

这样既符合UniqueBufferPointer的独占特性,又给编译器提供了合法的后备路径,同时NRVO依然可以正常触发(优化生效时不会实际调用移动构造)。

关于移动构造的补充疑问

当你声明移动构造后,编译器处理return f;时,会触发移动构造的隐式转换:C++标准规定,当返回的是函数内的局部对象时,即使是左值,编译器也允许将其视为右值来匹配移动构造——这是专门为返回值优化的后备场景设计的规则。简单来说,编译器会自动把return f;处理成return std::move(f);,因此能调用移动构造。

另外在/O2优化下,Visual Studio大概率会触发NRVO,此时移动构造不会被实际执行,只是代码必须保证它存在(作为优化失效的后备)。你可以在移动构造中添加打印语句验证:开启优化时不会输出日志,关闭优化时才会输出。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:55:24