5

考虑这样的事情:

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

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

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

4

3 回答 3

6

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

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

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


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

#include <atomic>

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

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

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

于 2018-01-29T13:30:53.953 回答
0

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

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

[class.copy.elision]/1

当满足某些条件时,允许实现省略类对象的复制/移动构造,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用。

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


1)constexpr对象除外

于 2018-01-29T13:33:11.223 回答
0

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

AFAIK 此描述是最新的,涵盖了您的主题copy_elision

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

然后是 NRVO 的描述(您提供)。而且,在下一个项目符号中,有一个匿名临时 RVO的描述,以及一个项目符号,自 c++17 以来这是强制性的。否则没有任何保证。

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

你能做的,就是做一个最小的例子,通过编译器分析汇编,看看优化是否启动,或者生成优化报告,然后看看你自己。


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

于 2018-01-29T13:37:22.503 回答