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

Can I rely on named return value optimisation for complicated return types?

考虑这样的事情:

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;
}

这种方法对于生成子集、排列等问题来说是相当标准的。但是,我试着画了一张图,说明 Return 鉴于类型的内部数据结构相当复杂,值优化应该在这里优化什么(std::unordered_multiset 是散列 table 而 std::set 是 'typically' 二叉搜索树)而且,我只能希望编译器比我聪明。

所以,谈论性能和(如果它很重要)C++14,我可以在这里 return 一个 SetOfSets 还是我应该通过引用将其作为输出参数传递?

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

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

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


从技术上讲是正确的,IMO,C++17 中没有 RVO,因为当 prvalue 被 returned 时,没有临时物化为 moved/copied。规则有点不同,但效果或多或少是一样的。或者,甚至更强,因为在 C++17 中不需要 copy/move 构造函数按值 return 纯右值:

#include <atomic>

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

int main() {
  std::atomic<int> i = f();
}

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

So, talking performance and (in case it matters) C++14, can I return a SetOfSets here or should I just pass it by reference as an out parameter?

如果您使用一个不错的编译器,您可以安全地 return 按值进行编译,因为会发生复制省略。但。但是不能保证复制省略1,你的编译器可能只有在给出正确的优化标志时才会这样做。

[class.copy.elision]/1

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.

这意味着如果您的程序在不执行的情况下仍然有意义,那么您应该只依赖复制省略。


1) 除了 constexpr 个对象

不是真的。这确实很常见,但不能保证,所以这完全取决于您所需的置信度。

据我所知,此描述是最新的并且涵盖了您的主题 copy_elision

关键语句是:“在以下情况下,允许编译器,但不要求省略[的复制和移动构造class 对象,即使 copy/move 构造函数和析构函数具有可观察到的副作用。"

然后是对 NRVO 的描述(你出示的)。并且,在下一个项目符号中有 anonymous temporary RVO 的描述,还有一个项目符号 自 c++17 起是强制性的。否则保证none。

不管类型有多复杂,无论“复杂”是什么意思。这些优化基于变量的状态启动,例如:“一个无名的临时对象,未绑定到任何引用,将被复制或移动到相同类型的对象中(忽略顶级 cv 限定) ”。请注意,none 与变量类型的内部工作有关,而是与使用它的上下文有关。

你可以做的是做一个最小的例子,然后通过编译器分析汇编,看看优化是否开始,或者 generate optimization report,然后你自己看看。


唯一的参考是明确声明允许优化对构造和销毁有副作用的类型。但这作为例外的授权条款。对于没有副作用的类型,默认情况下已经可以了。