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

为何同一函数两次调用一次触发NRVO,另一次却不触发?

为何NRVO未触发,反而调用了移动构造函数?

代码背景

定义了一个vector结构体,包含:

  • 公有成员X和Y
  • 带参构造函数:调用时打印integer constructor called
  • 复制构造函数:调用时打印copy constructor called
  • 移动构造函数:调用时打印move constructor called <== This line is different

函数makeVectorx5y5IfNotZero逻辑:

  • 参数Arg为true时,返回成员为{5,5}的vector对象
  • 参数Arg为false时,返回成员为{2,2}的vector对象

main函数中:

  • 通过makeVectorx5y5IfNotZero(1)构造vector A
  • 通过makeVectorx5y5IfNotZero(0)构造vector B

完整代码

#include <iostream>

struct vector{
    int X, Y;
    vector(int X, int Y) : X(X), Y(Y){
        printf("integer constructor called\n");
    }
    vector(const vector& other): X(other.X), Y(other.Y){
        printf("copy constructor called\n");
    }
    vector(vector&& other): X(other.X), Y(other.Y){
        printf("move constructor called      <== This line is different\n\n");
    }
};

vector makeVectorx5y5IfNotZero(bool Arg){
    if(Arg){
        vector A(0, 0);
        A.X = 5;
        A.Y = 5;
        return A;
    }
    else {
        vector B(1,1);
        B.X = 2;
        B.Y = 2;
        return B;
    }
}

int main(){
    printf("Making Vector\n");
    vector A = makeVectorx5y5IfNotZero(1);
    printf("Made Vector\n");
    printf("Making Another Vector\n");
    vector B = makeVectorx5y5IfNotZero(0);
    printf("Made Vector\n");
}

实际输出

Making Vector
integer constructor called
Made Vector
Making Another Vector
integer constructor called
move constructor called      <== This line is different

Made Vector

预期输出(基于NRVO)

Making Vector
integer constructor called
Made Vector
Making Another Vector
integer constructor called
Made Vector

问题

为何makeVectorx5y5IfNotZero()中的局部变量B会被移动到main()的局部变量B中,而非通过NRVO直接在同一内存地址构造单个变量?

编译器版本:

g++ (Rev3, Built by MSYS2 project) 14.1.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

解答

NRVO(具名返回值优化)的生效有严格条件:函数所有return路径必须返回同一个具名局部对象。

在你的代码中,makeVectorx5y5IfNotZero函数的两个分支分别返回了不同的局部变量A和B——这两个是完全独立的对象,编译器无法将它们合并到同一个内存地址(即main中目标对象的地址)进行构造,因此无法触发NRVO。

当NRVO无法生效时,C++11及以后的标准会优先触发移动构造:因为函数返回的局部对象是即将销毁的临时对象(右值),编译器会自动将其转换为右值引用,调用移动构造函数而非复制构造函数,这就是你看到输出中出现移动构造日志的原因。

如果要让NRVO生效,可以修改函数逻辑,让所有分支返回同一个具名对象,比如:

vector makeVectorx5y5IfNotZero(bool Arg){
    vector res(0, 0);
    if(Arg){
        res.X = 5;
        res.Y = 5;
    }
    else {
        res.X = 2;
        res.Y = 2;
    }
    return res;
}

此时所有return路径都返回同一个res对象,编译器就能正常触发NRVO,避免移动或复制构造的调用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 15:13:10