C++14中复杂返回类型能否依赖具名返回值优化?
在C++14中递归返回复杂容器 vs 使用引用输出参数的性能考量
首先,先把你给出的递归实现代码贴出来,方便讨论:
typedef std::unordered_multiset<int> Set; typedef std::set<Set> SetOfSets; SetOfSets somethingRecursive(SomeType somethingToAnalyze) { Set s; // ... // check base cases, reduce somethingToAnalyze, fill in s // ... SetOfSets ss = somethingRecursive(somethingToAnalyze); ss.insert(s); return ss; }
你的核心疑问是:因为SetOfSets的内部结构(嵌套了哈希表和二叉搜索树)比较复杂,不确定返回值优化(RVO/NRVO)的效果,纠结是直接返回类型还是改用引用输出参数。
先看C++14的返回值优化规则
在C++14中,**命名返回值优化(NRVO)**是编译器允许但不强制的优化,但主流编译器(GCC、Clang、MSVC)在你的这种场景下都会主动触发NRVO:
- 你的函数中始终返回同一个命名局部对象
ss,没有分支返回不同对象的情况,完全符合NRVO的触发条件。 - NRVO生效时,编译器会直接在调用者的栈帧上构造
ss,彻底避免任何拷贝或移动操作,性能和引用传参几乎没有区别。
退一步说,就算NRVO没有触发(比如某些极端的编译器或代码路径),C++11及以后的标准会自动将局部对象的返回转换成移动构造——对于std::set和std::unordered_multiset这类容器,移动构造的代价极低,只是转移内部指针和控制块的所有权,不会拷贝容器里的元素,性能开销可以忽略。
再对比引用输出参数的优劣
改用引用传参作为输出参数(比如void somethingRecursive(SomeType somethingToAnalyze, SetOfSets& out))确实能彻底消除拷贝/移动的可能性,但它的缺点也很明显:
- 可读性差:递归函数的语义变得不直观,调用者需要提前创建好容器并传入,递归过程中还要维护这个外部容器的状态,代码的函数式表达能力被削弱。
- 易出错:如果递归逻辑复杂,不小心在多个分支中重复修改输出参数,或者传入未初始化的容器,容易引入隐蔽的bug。
给你的具体建议
- 优先保留直接返回
SetOfSets的写法:代码更清晰、更符合递归函数的语义,而且在主流编译器的优化选项(比如-O2)下,性能和引用传参几乎无差别。- 注意不要手动给返回值加
std::move(比如return std::move(ss);),这会抑制NRVO的触发,反而导致不必要的移动操作。
- 注意不要手动给返回值加
- 如果确实担心性能,做实际测试:用编译器的优化编译选项构建程序,通过查看汇编代码或者性能分析工具(比如
perf)对比两种写法的实际运行时间,理论分析终究不如实测可靠。
内容的提问来源于stack exchange,提问作者sigil
相关产品推荐
相关产品推荐

