我有以下比较方法。 方法比较并返回diff结果。
我希望最小化结果列表被复制的次数(时间和分配)。
这样做的一种方法是为结果添加额外的引用参数,但我喜欢utils函数是关闭的(它们不会改变值),所以我宁愿避免这种情况。
使用const& amp;可以避免一个副本。在作业中
const& list<uint32> diff = getDiffNewElements (...)
,可以通过一种方法避免本地复制到临时吗?
diff方法:
list<uint32> getDiffNewElements(const list<Row>& src ,const list<Row>& dst) {
list<uint32> result;
... Do Some compare
return result;
}
答案 0 :(得分:2)
您提供的代码中可能有两个副本。函数内部的一个,从变量result
到返回的对象。如果功能的复杂性允许,NRVO将负责这一点。如果看起来有一个return语句,那么编译器将忽略该副本(假设您没有使用编译器标志禁用它并且您已启用某些优化级别)。
第二个潜在副本位于调用者中,从返回值到最终存储。编译器几乎总是忽略该副本,并且在此处比在NRVO情况下更简单:调用约定(我知道的所有调用约定)确定调用者为返回的对象保留空间,并且它将指向该位置的隐藏指针传递给该函数。该函数依次使用该指针作为第一个副本的目标。
函数T f()
转换为void f( uninitialized<T>* __ret )
(没有uninitialized<>
这样的东西,但是请耐心等待)并且函数的实现在复制时使用该指针作为目标在return
声明中。在调用者站点上,如果您有T a = f();
,编译器会将其转换为T a/*no construction here*/; f(&a);
问题中有一些有趣的代码似乎表明你过去曾误导过:const list<uint32>& diff = getDiffNewElements(...)
。使用引用而不是直接存储值(如在list<uint32> diff = getDiffNewElements(...)
中)对所创建的副本数量没有任何影响。 getDiffNewElements
仍然需要复制(或删除副本)到返回的对象,并且该对象位于调用者的范围内。通过获取const引用,您告诉编译器您不想直接命名该对象,而是将其保留为范围中的未命名对象,并且您只想使用对它的引用。从语义上讲,它可以转化为:
T __unnamed = getDiffNewElements(...); // this copy can be elided
T const& diff = __unnamed;
编译器是免费的,并且可能会使用标识符diff
作为__unnamed
的别名来优化引用,而不需要额外的空间,因此通常它不会比替代方案更糟糕,但代码稍微复杂一点,没有任何优势。
很久以前,当我有时间的时候,我开始写博客,写了几篇关于价值语义的文章,(N)RVO和复制文章。你可能想看看。
答案 1 :(得分:0)
你正朝着错误的方向前进。您的getDiffNewElements
方法不会创建不必要的副本,因为现代编译器会RVO(注释中指出了这一点,但如果您的编译器没有执行RVO,那么您无能为力, const&
无济于事。优化此函数的方法是返回vector
而不是list
,因为您可以在reserve
上调用vector
并在每次push_back
时避免内存分配一个新元素。 list
没有使用默认分配器预分配内存,并且没有reserve
方法来执行此操作。