Правильный способ (переместить семантику) для возврата std::vector из вызова функции в C++11

Я хочу заполнить std::vector (или какой-нибудь другой контейнер STL):

class Foo {
public:
  Foo(int _n, const Bar &_m);
private:
  std::vector<Foo> fooes_;
}

1. Хорошо выглядящий ctor, высокая производительность

std::vector<Foo> get_vector(int _n, const Bar &_m) {
  std::vector<Foo> ret;
  ... // filling ret depending from arguments
  return ret;
}

Foo::Foo(int _n, const Bar &_m) : fooes_(get_vector(_n, _m) {}

2. Лучшая производительность, хуже выглядящий ctor

void fill_vector(int _n, const Bar &_m, std::vector<Foo> &_ret) {
  ... // filling ret depending from arguments
}

Foo::Foo(int _n, const Bar &_m) { fill_vector(_n, _m, fooes_); }

Можно ли переписать get_vector функция из первого примера с C++0x (переместить функции семантики и т. д.), чтобы избежать избыточного копирования и вызовов конструктора?

3 ответа

Решение

Если вы используете C++0x-совместимый компилятор и стандартную библиотеку, вы получите лучшую производительность из первого примера , ничего не делая. Возвращаемое значение get_vector(_n, _m) является временным, и конструктор перемещения для std::vector (конструктор, принимающий ссылку на rvalue) будет вызван автоматически без дальнейшей работы с вашей стороны.

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

Я считаю, что (1) и (2) имеют одинаковую производительность даже без C++0x, если ваш компилятор выполняет оптимизацию именованных возвращаемых значений, что, как я полагаю, большинство делает. Ни копий, ни ходов не должно быть.

Пожалуйста, поправьте меня, если я ошибаюсь, потому что, если это так, я неправильно понимаю NRVO.

В конкретном случае, который вы рассматриваете, первая реализация так же эффективна, как и вторая. Компилятор оптимизирует копию ret в get_vector функция к возвращаемому значению, и она будет использовать семантику перемещения для передачи владения вектором классу контейнера. Конструкция перемещения в векторе требует (зависит от реализации, но хорошее приближение) 3 копии указателя, независимо от количества и размеров элементов в контейнере. Передача вектора в качестве ссылки для изменения требует одной копии указателя (снова приблизительная стоимость), но любая операция, которую вы выполняете над вектором, будет доминировать над стоимостью любого из вариантов.

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

Другие вопросы по тегам