nrvo能否触发取决于函数是否仅含一个return语句且返回同名局部对象;const/引用返回类型、多分支return、编译器差异及副作用代码均会禁用或削弱其效果。

NRVO 能不能被触发,取决于函数体里怎么写 return
NRVO 不是编译器“尽力而为”的优化,而是有明确触发条件的:函数必须只有一个 return 语句,且返回的是一个**同名的局部对象**。比如:
MyClass create() {
MyClass obj;
// ... 构造逻辑
return obj; // ✅ 符合 NRVO 条件
}如果改成 return MyClass(); 或 return make_obj();,就不是具名对象,NRVO 失效;如果函数里有两个 return obj;(比如在 if/else 分支中),多数编译器也会放弃 NRVO——哪怕两个分支返回的是同一个变量名。
为什么加了 const 或引用修饰反而禁用 NRVO
NRVO 的前提是编译器能安全地把返回值“就地构造”到调用方提供的内存位置。一旦你写成 const MyClass& create() 或 MyClass&& create(),返回类型不再是值类型,编译器无法做就地构造:它必须先构造临时对象,再绑定引用,这反而强制产生拷贝或移动。更隐蔽的是,连 auto&& x = create(); 这种调用,如果 create() 返回值类型被意外推导成引用,也会间接破坏 NRVO 的生效前提。
不同编译器对 NRVO 的支持程度差异很大
Clang 默认开启 NRVO,且在 -O1 及以上基本稳定触发;GCC 从 7.1 开始对单 return 场景较可靠,但遇到内联展开或模板实例化时可能退化;MSVC 在 /O2 下支持较好,但若函数含异常处理块(try/catch),哪怕没抛异常,也常导致 NRVO 被跳过。验证是否生效,最直接的方式是给 MyClass 加带输出的移动构造函数,运行时看它是否被调用——没调用,大概率 NRVO 成功了。
别为了 NRVO 牺牲可读性或引入副作用
有人会把多路逻辑硬塞进一个 obj 变量,比如:
MyClass create(bool flag) {
MyClass obj;
if (flag) {
obj = MyClass(1);
} else {
obj = MyClass(2);
}
return obj; // ✅ NRVO 可能成立
}这看似满足条件,但 obj 实际被默认构造又赋值两次,开销未必比直接 return MyClass(1) 小。更糟的是,如果 MyClass 的默认构造有副作用(比如日志、资源注册),这种写法就引入了不该有的行为。NRVO 是锦上添花,不是兜底方案;优先让代码语义清晰,再看编译器能不能省掉那一次移动。
真正容易被忽略的是:NRVO 只作用于**命名对象**,和移动语义无关;它不改变程序行为,但一旦失效,你看到的可能是多一次移动构造——而这个移动构造如果没被定义或被删掉(= delete),就会在链接期才报错,而不是编译期。










