如果我有以下函数声明,它按值返回std::list<Triangle*>
:
std::list<Triangle*> getAllAbove(Triangle* t);
当我在getAllAbove结束时返回std::list<Triangle*>
(在getAllAbove中的堆栈上创建)时,GCC是否能够优化对std::list<Triangle*>
的复制构造函数的调用(这可能是会迭代所有元素并复制它们,或者至少只复制列表元数据(例如不复制元素本身)?该列表可能包含几千个指针,我想避免不必要地复制所有这些指针。
是唯一可以绕过复制构造函数调用以在堆上创建列表然后返回指向它的指针的方法吗?
答案 0 :(得分:6)
嗯,绕过复制的另一种方法是使用“return parameter”惯用法而不是使用函数的返回值
void getAllAbove(Triangle* t, std::list<Triangle*>& result);
不要像现在那样“在堆栈上”形成结果,而是直接在result
参数中形成它(即在您从调用者传递的收件人列表中)。
对于原始代码,是否进行复制取决于编译器的功能。
从最抽象的角度来看,您的代码实际上有两个副本。 (是的,当列表的整个内容在另一个列表中逐个元素地重复复制时,这些都是完整的复制。)首先,在函数内的堆栈上创建的命名列表对象被复制到无名的临时对象保存函数的返回值。然后将临时对象复制到调用代码中的最终收件人对象。大多数编译器都能够消除其中一个拷贝(消除C ++ 98规范允许的中间临时拷贝)。
为了消除第二个,编译器必须能够执行所谓的命名返回值优化(如C ++ 03规范所允许的)。支持命名返回值优化的编译器应该能够基本上隐式地将函数的接口转换为等效的上述“返回参数”惯用法。我希望GCC能够做到这一点。试一试,看看会发生什么。
答案 1 :(得分:4)
通常您不必担心,除非您有理由:Want Speed? Pass by Value.
答案 2 :(得分:2)
以最容易理解和维护的方式编写代码。然后简介。 如果这是一个问题点,请通过删除副本来优化它,否则保持原样。
我希望我的编译器能够将返回值优化应用于此,但不需要手动干预。我希望我的编译器的下一个版本能够使所有这些无关紧要,因为它将move semantics应用于问题,将容器的复制(将其所有值复制)转换为两个指针的简单复制。
答案 3 :(得分:0)
为了摆脱对编译器优化的担忧,您可以通过引用传递list<>
。
std::list<Triangle*>& getAllAbove(Triangle* t, std::list<Triangle*>& myList);
您可以通过引用返回,也可以不返回它。
答案 4 :(得分:0)
您可以使用void getAllAbove(Triangle* t, std::list<Triangle*>& out)
。填写一次列表并获取out