对于复杂的返回类型,我可以依赖命名返回值优化吗?

sig*_*gil 5 c++ rvo nrvo

考虑这样的事情:

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;
}
Run Code Online (Sandbox Code Playgroud)

这种方法对于生成子集、排列等问题来说是相当标准的。 但是,我尝试制作一个图表,说明返回值优化应该在这里优化什么,因为该类型的内部数据结构相当复杂(std::unordered_multiset是一个哈希表,并且std::set通常是' 一个二叉搜索树),好吧,我只能希望编译器比我更聪明。

那么,谈论性能和(如果重要的话)C++14,我可以在SetOfSets这里返回 a还是应该通过引用将它作为输出参数传递?

Dan*_*ica 6

在 C++17 之前,您根本不能依赖复制省略,因为它是可选的。然而,所有主流编译器很可能会应用它(例如,GCC 即使使用-O0优化标志也会应用它,-fno-elide-constructors如果你愿意,你需要明确地禁用复制省略)。

但是,std::set支持移动语义,因此即使没有 NRVO,您的代码也没有问题。

请注意,在 C++17 中,NRVO 也是可选的。RVO 是强制性的。


从技术上讲,IMO,C++17 中没有 RVO,因为当返回 prvalue 时,没有临时物化可以从中移动/复制。规则有点不同,但效果或多或少是一样的。或者,甚至更强,因为在 C++17 中不需要复制/移动构造函数按值返回纯右值:

#include <atomic>

std::atomic<int> f() {
  return std::atomic<int>{0};
}

int main() {
  std::atomic<int> i = f();
}
Run Code Online (Sandbox Code Playgroud)

在 C++14 中,此代码无法编译。

  • @Bathsheba 在一般情况下无法保证 NRVO,因为在一般情况下无法在对象构造时确定该对象是否将用作返回值。 (2认同)
  • 我不是语言律师,所以从技术上讲,我们可能不应该在 C++17 中谈论 RVO。来自 Cppreference:_C++17 纯右值和临时值的核心语言规范与早期的 C++ 修订版有根本的不同:​​不再有临时值可以复制/移动。描述 C++17 机制的另一种方式是“非物化值传递”:返回和使用纯右值而不会物化一个临时值。_ 我使用这个术语来表示它的效果。(请注意,C++11 标准中也没有提到 RVO。) (2认同)