为何同一函数两次调用一次触发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
相关产品推荐
相关产品推荐

