考虑这样的事情:
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参数传递?
答案 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,并看到你自己。
†唯一的参考是明确声明,对于具有构造和破坏副作用的类型,允许进行优化。但这是一个特殊的授权条款。对于没有副作用的类型,默认情况下已经可以使用。