Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果我通过值从函数返回STL容器,GCC会单独复制所有元素吗?_C++_Gcc_Stl_G++ - Fatal编程技术网

C++ 如果我通过值从函数返回STL容器,GCC会单独复制所有元素吗?

C++ 如果我通过值从函数返回STL容器,GCC会单独复制所有元素吗?,c++,gcc,stl,g++,C++,Gcc,Stl,G++,如果我有以下函数声明,它按值返回std::list: std::list<Triangle*> getAllAbove(Triangle* t); std::list getAllAbove(三角形*t); 当我在getAllAbove的末尾返回std::list(在getAllAbove中的堆栈上创建)时,GCC是否能够优化对std::list的复制构造函数的调用(该构造函数可能会迭代所有元素并复制它们),或者至少只复制列表元数据(例如,不是元素本身)?列表可能包含数千个指针,

如果我有以下函数声明,它按值返回
std::list

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


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

为了避免担心编译器优化,可以通过引用函数传递
list

std::list<Triangle*>& getAllAbove(Triangle* t, std::list<Triangle*>& myList);
std::list和getAllAbove(三角形*t,std::list和myList);

您可以通过引用返回,也可以不返回。

好的,绕过复制的另一种方法是使用“return parameter”习惯用法,而不是使用函数的返回值

void getAllAbove(Triangle* t, std::list<Triangle*>& result);
void getAllAbove(三角形*t,标准::列表和结果);
不要像现在那样在堆栈上形成结果,而是直接在
result
参数中形成它(即,在从调用者传递的收件人列表中)

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


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


为了消除第二个问题,编译器必须能够执行所谓的命名返回值优化(C++03规范允许)。支持命名返回值优化的编译器应该能够本质上隐式地将函数的接口转换为上述“返回参数”的等价物习惯用法。我希望GCC能够做到。试试看会发生什么。

您可以使用
void getAllAbove(Triangle*t,std::list&out)
。填写一次列表,然后将输出输入到
中,通常您不需要担心,除非您有理由:

以最容易理解和维护的方式编写代码。然后进行分析。如果这是一个问题点,请通过删除副本对其进行优化,否则保持原样


不过,我希望我的编译器对其应用返回值优化,这样就不需要手动干预。我希望我的编译器的下一个版本将所有这些都变得无关紧要,因为它适用于这个问题,将容器的复制(复制其所有值)变成两个指针的简单复制。

另请参见“大多数编译器将能够消除其中一个复制”-如果初始化器中使用了
getAllAbove
的结果。如果在副本分配中使用了它,则无法消除它。因此
std::list foo=getAllAbove(bar);
是好的。
foo=getAllAbove(bar);
是坏的。
getAllAbove(bar)。swap(foo)
是一种避免错误的解决方法,无需更改函数的签名。C++0x move语义是错误的修复方法。@Steve Jessop:我明白你的意思,但我不明白你为什么要将它附加到我的特定语句中。该语句引用C++98省略,它允许从命名的本地删除复制对象到中间临时对象。无论函数是在复制分配中使用还是在初始化中使用,都可以消除该复制。即,不进行复制,而是延长命名本地对象的生存期,以便将其用作分配中的RHS。这是在分配内容中出现问题的第二次消除xt.第一个总是可能的。@AndreyT:我可能只是搞不清楚你指的是哪一个是大多数编译器可以消除的。实际上,我希望大多数编译器都可以消除这两个。正如你所说,按时间顺序排列的第一个副本,NRVO,总是适用的。临时(或命名对象,如果发生了NRVO)如果是复制ctor,则最终目的地可以省略,如果是复制赋值,则不能省略。我认为您的意思是“消除中间临时对象,如C++98中所述”“@Steve Jessop:这实际上是一个有趣的问题。C++98允许在初始化上下文中复制临时文件时进行复制省略。但是,除此之外,C++98还允许在
return
上下文中复制临时文件时进行复制省略。我不清楚C++98是否允许同时应用这两种省略(在“返回值用作初始值设定项”上下文。C++03明确指出两者都可以应用,而正如我所相信的,C++98希望编译器只选择其中一个(在初始化上下文中)。在赋值上下文中,C++98将被迫选择第二个,因为这是唯一可用的。这就是我的“Most[C++98]的意思编译器将能够消除其中一种复制”。