Если я верну контейнер STL из функции по значению, будет ли GCC копировать все элементы по отдельности? - PullRequest
3 голосов
/ 08 июня 2011

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

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

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

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

Ответы [ 5 ]

6 голосов
/ 08 июня 2011

Что ж, альтернативный способ обойти копирование - использовать идиому «возвращаемый параметр» вместо использования возвращаемого значения функции

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

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

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

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

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

4 голосов
/ 08 июня 2011

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

2 голосов
/ 08 июня 2011

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

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

0 голосов
/ 08 июня 2011

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

0 голосов
/ 08 июня 2011

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

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

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

...