Если я возвращу контейнер STL из функции по значению, GCC будет копировать все элементы отдельно?

Если у меня есть следующее объявление функции, которое возвращает значение std::list по значению:

std::list<triangle*> getAllAbove(Triangle* t);
</triangle*>

когда я возвращаю std::list (который создается в стеке в getAllAbove) в конце getAllAbove, GCC сможет оптимизировать вызов конструктора std::list copy (который предположительно будет проходить через все элементы и скопировать их) или, по крайней мере, скопировать только метаданные списка (например, не сами элементы)? Список может потенциально содержать несколько тысяч указателей, и я бы хотел избежать ненужного копирования всех них.

Единственный способ обойти вызов конструктора копирования для создания списка в куче, а затем вернуть указатель на него?

5 ответов

Ну, альтернативный способ обойти копирование - использовать идиому "return parameter" вместо использования возвращаемого значения функции

void getAllAbove(Triangle* t, std::list<triangle*>& result);
</triangle*>

Вместо того, чтобы формировать результат "в стеке", как сейчас, формируйте его непосредственно в параметре result (т.е. в списке получателей, который вы передаете от вызывающего).

Что касается вашего исходного кода, произойдет ли копирование или нет, зависит от возможностей вашего компилятора.

С самой абстрактной точки зрения ваш код фактически имеет две копии. (И да, это полноценная копия, когда весь контент списка тщательно дублируется в другом списке, элемент за элементом.) Во-первых, именованный объект списка, который вы создаете в стек внутри вашей функции, копируется в безымянный временный объект который содержит возвращаемое значение функции. Затем временный объект копируется в конечный объект получателя в вызывающем коде. Большинство компиляторов смогут устранить одну из этих копий (чтобы исключить промежуточные временные, что разрешено спецификацией С++ 98).

Чтобы устранить второй, компилятор должен иметь возможность выполнять так называемую Оптимизацию возвращаемого значения имен (как это допускается спецификацией С++ 03). Компилятор, который поддерживает оптимизацию с наименьшим значением возвращаемого значения, должен иметь возможность существенно неявно преобразовывать ваш функциональный интерфейс в эквивалент вышеупомянутой идиомы "return parameter". Я бы ожидал, что GCC сможет это сделать. Попробуйте и посмотрите, что произойдет.


Как правило, вам не нужно беспокоиться, если у вас нет причин: Хотите скорость? Перейдите по значению.


Напишите код таким образом, который кажется самым легким для понимания и поддержания его. Затем профиль. Если это проблема, оптимизируйте ее, исключив копию, иначе оставьте ее так, как она есть.

Я бы ожидал, что мой компилятор применит оптимизацию возвращаемого значения к этому, хотя, делая ручное вмешательство ненужным. И я ожидаю, что следующая версия моего компилятора сделает все это нерелевантным, потому что оно применит переместить семантику в проблему, повернув копирование контейнера (копируя все его значения) в простое копирование двух указателей.


Вы можете пойти с void getAllAbove(Triangle* t, std::list<triangle*>& out)</triangle*>. Заполните список один раз и получите вывод в out


Чтобы избавиться от беспокойства по поводу оптимизации компилятора, вы можете передать list<> ссылкой на функцию.

std::list<triangle*>& getAllAbove(Triangle* t, std::list<triangle*>& myList);
</triangle*></triangle*>

Вы можете либо вернуться по ссылке, либо просто не вернуть его.

licensed under cc by-sa 3.0 with attribution.