Возврат c ++ std :: vector без копии?

Можно ли вернуть стандартный контейнер из функции без копирования?

Пример кода:

std::vector<A> MyFunc();

...

std::vector<A> b = MyFunc();

Насколько я понимаю, это копирует возвращаемое значение в новый вектор b. Позволяет ли функция, возвращающая ссылки, или что-то подобное, избежать копирования?


person static_rtti    schedule 15.09.2010    source источник
comment
Дубликат stackoverflow.com/questions/3703302 /?   -  person Andy Thomas    schedule 16.09.2010


Ответы (3)


Если ваш компилятор поддерживает NRVO, то копия не будет сделана при соблюдении определенных условий в функции, возвращающей объект. К счастью, это наконец-то было добавлено в Visual C ++ 2005 (v8.0 ) Это может иметь большое положительное влияние на производительность, если контейнер большой, очевидно.

Если в документации вашего собственного компилятора не указано, поддерживается он или нет, вы сможете скомпилировать код C ++ на ассемблер (в режиме оптимизации / выпуска) и проверить, что было сделано, с помощью простой функции-образца.

Также есть отличное более широкое обсуждение здесь

person Steve Townsend    schedule 15.09.2010
comment
Спасибо! Есть идеи о NRVO в gcc? - person static_rtti; 16.09.2010
comment
@static_rtti - мне придется довериться людям Linux для этого, из-за боязни попасть в рот - person Steve Townsend; 16.09.2010
comment
Вы можете легко протестировать с помощью класса, который отслеживает в Copy CTor. Обратите внимание, что это может быть оптимизация, которая не обязательно сработает, например. с другими именами возвращаемых объектов. Однако с помощью ссылок C ++ 0x rvalue классы могут реализовать гарантию. Вы можете ожидать этого для стандартных контейнеров в современном STL. - person peterchen; 16.09.2010
comment
AFAIK, с GCC вам придется отключить эту оптимизацию с помощью -fno-elide-constructors, поскольку в противном случае она включена даже с -O0. - person UncleBens; 16.09.2010
comment
@static_rtti: GCC очень хорошо удаляет ненужные копии. Насколько я знаю, нет другого компилятора лучше в этом - точно так же хорошо или хуже. - person sellibitze; 16.09.2010
comment
Согласитесь с Петерхеном, существует ряд ситуаций, в которых NRVO не сработает, которые описаны в вашем связанном документе. Я думаю, что ответ должен обсуждать семантику перемещения C ++ 0x, чтобы заслужить наибольшее количество голосов и выбранный ответ, поэтому, к сожалению, я голосую против этого. Компилятору никогда не требуется выполнять какую-либо оптимизацию - с семантикой перемещения вы можете потребовать, чтобы компилятор не делал копию. - person AshleysBrain; 16.09.2010

Rvalues ​​(«временные»), привязанные к const ссылкам, будут иметь время жизни, продленное до конца времени жизни ссылки. Поэтому, если вам не нужно изменять этот вектор, подойдет следующее:

const std::vector<A>& b = MyFunc();

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

В противном случае полагайтесь на C ++ 1x с его ссылками на rvalue и перемещайте семантику «очень скоро» и оптимизируйте эту копию без необходимости что-либо делать.

person sbi    schedule 15.09.2010

Если вы можете изменить подпись функции, вы можете использовать

std::vector<A>& MyFunc(); 

or

void MyFunc(std::vector<A>& vect);

Вы также можете вернуть умный указатель, но это предполагает создание нового объекта.

some_smart_pointer<std::vector<A>> MyFunc();

HTH

person beezler    schedule 15.09.2010
comment
Первый, скорее всего, не сработает, если вы возвращаете вектор, который является локальным в функции. Хотя со вторым все в порядке - person jcoder; 15.09.2010
comment
+1 Потому что вторая форма - это обычный способ сделать это. Проблема с первым примером std::vector<A>& MyFunc(); заключается в том, что вектор должен быть где-то размещен - MyFunc не может разместить его в стеке и вернуть ссылку. Это возможно, если вектор является членом класса, а вы просто возвращаете ссылку на существующий вектор. - person Eclipse; 15.09.2010
comment
@John Burton / @ beezler - мягко говоря, «не сработает», если предположить, что возвращаемый контейнер основан на стеке в вызываемой функции. - person Steve Townsend; 16.09.2010
comment
@ Джон Бертон / @ Стив Таунсенд: почему именно это не сработает? - person static_rtti; 16.09.2010
comment
@static_rtti: если объект размещен в стеке, он выйдет из области видимости, когда функция вернется. Итак, у вас осталась ссылка на объект, которого больше не существует. - person Dennis Zickefoose; 16.09.2010
comment
Потому что он будет уничтожен, когда функция завершится, поэтому вы вернете ссылку на переменную, которая больше не существует. Ваш компилятор, скорее всего, предупредит вас, хотя, по крайней мере, - person jcoder; 16.09.2010