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

时间:2018-01-29 12:39:21

标签: 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;
}

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

那么,谈论表现和(如果重要)C++14,我可以在这里返回SetOfSets,还是应该通过引用将其作为out参数传递?

3 个答案:

答案 0 :(得分:4)

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

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

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

从技术上讲,IMO在C ++ 17中没有RVO,因为当返回prvalue时,没有临时实现移动/复制。规则有点不同,但效果或多或少相同。或者,甚至更强,因为在C ++ 17中不需要复制/移动构造函数来按值返回prvalue:

#include <atomic>

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

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

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

答案 1 :(得分:0)

  

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

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

  

W3C Recommendation

     

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

这意味着如果你的程序没有执行,你应该只依赖于复制省略。

1)除了<body>个对象

答案 2 :(得分:0)

不是真的。它确实很常见,但不能保证,所以这一切都取决于你所需的置信水平。

AFAIK此说明是最新的,涵盖了您的主题copy_elision

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

然后是NRVO(你提出的)的描述。并且,在下一个项目符号中有一个匿名临时RVO 的描述,以及一个从c ++ 17开始强制执行此的项目符号。否则无法保证。

类型是多么复杂,无论&#34; 复杂&#34;手段。这些优化基于变量的状态启动,例如:&#34; 无名的临时,不绑定到任何引用,将被复制或移动到相同类型的对象中(忽略顶级cv资格)&#34 ;.请注意,这些都与变量类型的内部工作无关,而是与使用它的上下文有关。

你可以做的是制作一个最小的例子,并通过编译器和分析程序集来查看优化是否已经启动,或generate optimization report,并看到你自己。

唯一的参考是明确声明,对于具有构造和破坏副作用的类型,允许进行优化。但这是一个特殊的授权条款。对于没有副作用的类型,默认情况下已经可以使用。