Подтвердить что ты не робот

С++ накапливается с перемещением вместо копирования

У меня есть следующий код

auto adder = [](string& s1, const string& s2)->string&&
   {
      if (!s1.empty())
         s1 += " ";
      s1 += s2;
      return move(s1);
   };

   string test;
   test.reserve(wordArray.size() * 10);
   string words = accumulate(wordArray.begin(), wordArray.end(), 
       move(test), adder);

Что я хотел бы здесь, так это избежать строкового копирования. К сожалению, это не достигается в результате реализации vs2012. Внутри накапливается вызов другой функции _Accumulate и функция rvalue теряется в процессе.

Вместо этого я вместо этого называю функцию _Cccumulate

string words = _Accumulate(wordArray.begin(), wordArray.end(), 
    move(test), adder);

Я получаю предполагаемое увеличение производительности.

Должна ли переписываться библиотека std для принятия аргументов rvalue?

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

4b9b3361

Ответ 1

Проверяя один из недавних черновиков Post С++ 11 (N3337.pdf), мы видим, что эффект std:: accumulate определяется как

Вычисляет его результат путем инициализации аккумулятора acc с начальным значением init, а затем изменяет его с помощью acc = acc + * я или acc = binary_op (acc, * i) для каждого итератора я в диапазоне [первый, последний] в порядке.

Итак, стандарт фактически запрещает реализации, которые используют std:: move для старого значения аккумулятора следующим образом:

template <class InputIterator, class T, class BinOp>
T accumulate (InputIterator first, InputIterator last, T init, BinOp binop)
{
  while (first!=last) {
    init = binop(std::move(init), *first);
    ++first;
  }
  return init;
}

что является неудачным в вашем случае.

Вариант (1): Внедрите это управление движением, накапливайте себя.

Опция (2): продолжайте использовать функтор, например

struct mutating_string_adder {
  string operator()(string const& a, string const& b) const {return a+b;}
  string operator()(string & a, string const& b)      const {a += b; return std::move(a);}
  string operator()(string && a, string const& b)     const {a += b; return std::move(a);}
};

Обратите внимание, что здесь я не использовал типы возвращаемых ссылок rvalue. Это преднамеренно, так как может избежать оборванных опорных проблем, например, в случае, когда выбрана последняя перегрузка, и "а" инициализируется для обращения к временному объекту. Все операторы + перегрузки для строк также намеренно возвращаются по значению.

Кроме того, вы можете использовать std:: copy в сочетании с std:: stringstream и итератором выходного потока.

Добавление: Альтернативный mutating_string_adder с некоторой частичной совершенной пересылкой:

struct mutating_string_adder {
  template<class T, class U>
  std::string operator()(T && a, U && b) const {
    return std::move(a) + std::forward<U>(b);
  }
};