如果我按函数从函数返回一个STL容器,GCC会单独复制所有元素吗?

Wir*_*uce 3 c++ gcc stl g++

如果我有以下函数声明返回一个std::list<Triangle*>by值:

std::list<Triangle*> getAllAbove(Triangle* t);
Run Code Online (Sandbox Code Playgroud)

当我std::list<Triangle*>在getAllAbove结束时返回(在getAllAbove中的堆栈上创建)时,GCC是否能够优化对std::list<Triangle*>复制构造函数的调用(可能会迭代所有元素并复制它们),或者至少只复制列表元数据(例如不是元素本身)?该列表可能包含几千个指针,我想避免不必要地复制所有这些指针.

绕过复制构造函数调用以在堆上创建列表然后返回指向它的指针的唯一方法是什么?

AnT*_*AnT 6

那么,绕过复制的另一种方法是使用"返回参数"习惯而不是使用函数的返回值

void getAllAbove(Triangle* t, std::list<Triangle*>& result);
Run Code Online (Sandbox Code Playgroud)

不像现在那样在堆栈上形成结果,而是直接在result参数中形成它(即在您从调用者传递的收件人列表中).

至于原始代码,是否进行复制取决于编译器的功能.

从最抽象的角度来看,您的代码实际上有两个副本.(是的,当列表的整个内容在另一个列表中逐个元素地重复复制时,这些都是完整的复制.)首先,在函数内的堆栈上创建的命名列表对象被复制到无名的临时对象保存函数的返回值.然后将临时对象复制到调用代码中的最终收件人对象.大多数编译器都能够消除其中一个拷贝(消除C++ 98规范允许的中间临时拷贝).

为了消除第二个,编译器必须能够执行所谓的命名返回值优化(如C++ 03规范所允许的).支持命名返回值优化的编译器应该能够基本上隐式地将函数的接口转换为等效的上述"返回参数"惯用法.我希望GCC能够做到这一点.尝试一下,看看会发生什么.

  • "大多数编译器都能够消除其中一个副本" - 如果在初始化程序中使用`getAllAbove`的结果.如果它用于复制分配,则无法消除.因此`std :: list <Triangle*> foo = getAllAbove(bar);`很好.`foo = getAllAbove(bar);`很糟糕.`getAllAbove(bar).swap(foo)`是一种避免坏的方法,无需更改函数的签名.C++ 0x移动语义是对坏的修复. (2认同)