Есть ли более эффективный способ установить std :: vector из потока?

В настоящее время я устанавливаю значениеstd::vector<char> изstd::ostringstream следующее:

void
foo(std::vector<char> &data, std::stringstream &stream) {
  data = std::vector<char>(stream.str().begin(), stream.str().end());
}

Мне интересно, есть ли более эффективный способ сделать это с помощью STL в C ++ или метод, который я здесь привожу, считается подходящим? Буду ли я лучше использоватьstd::stringstream вместо?

 Robᵩ30 мая 2012 г., 21:53
Я не уверен, насколько это эффективно, но этоявляетс неверно. Два вызова.str() вернуть разные объекты.
 WilliamKF30 мая 2012 г., 22:22
Спасибо за указание на эту ошибку, подумал, что я получаю ссылку, а не копию от str ().

Ответы на вопрос(4)

Ты можешь позвонитьreserve на вашейdata и используйте диапазонinsert участник прямо наdata вместо использования копирования-назначения. Вещь оvectorледует помнить, что каждый узел может увеличить размер (и переместить все элементы). Таким образом, вам лучше выделить память за один раз (если вы знаете, сколько объектов вы будете хранить - что вы знаете здесь) и использовать этот факт.

 ildjarn30 мая 2012 г., 22:22
@ KillianDS: вероятно, только для итераторов с произвольным доступом, в противном случае диапазон будет без необходимости проходиться дважды.
 Steve Jessop31 мая 2012 г., 00:38
@ ildjarm: и не обязательно для входных итераторов, которые нельзя пройти дважды.
 KillianDS30 мая 2012 г., 22:06
Я бы предположил, что большинство реализаций будет использоватьstd::distance вызовите конструктор на основе итератора, чтобы сделать эту логику самостоятельно.
Решение Вопроса

неопределенное поведение. stream.str() возвращает строку От стоимости, иначе временная строка. Вы беретеbegin итератор одного временного иend итератор другого, создающий недопустимый диапазон.

Один из способов преобразования потока в контейнер - использовать общий интерфейс итератора:

#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>

int main(){
  std::stringstream src("....");
  std::vector<char> dest;
  // for a bit of efficiency
  std::streampos beg = src.tellg();
  src.seekg(0, std::ios_base::end);
  std::streampos end = src.tellg();
  src.seekg(0, std::ios_base::beg);
  dest.reserve(end - beg);

  dest.assign(std::istreambuf_iterator<char>(src), std::istreambuf_iterator<char>());

  std::copy(dest.begin(), dest.end(), std::ostream_iterator<char>(std::cout));
}

Живой пример на Ideone.

Другой метод - кешировать возвращенныйstd::string объект:

std::string const& s = stream.str();
data.reserve(s.size());
data.assign(s.begin(), s.end());
 Xeo31 мая 2012 г., 00:51
@ Steve: Кажется, ты лучше меня, так как я даже не помнил эту функцию до сих пор. Не знаю, будет ли он работать так же.
 WilliamKF30 мая 2012 г., 22:26
Почему есть два вызова std :: copy ()? Они кажутся мне излишними.
 Steve Jessop31 мая 2012 г., 00:43
Полагаю, это слишком много, чтобы надеяться, чтоsrc.rdbuf()->in_avail() дает правильный размер без возни сseek? Я плохо разбираюсь в потоковых библиотеках.
 Xeo30 мая 2012 г., 22:28
@ WilliamKF: первый (теперь заменен на.assign) было вставить в вектор, второй выводит вstd::cout (слишком ленив для рукописного цикла).

ваш код неверен из-за двух вызововstr(). В целях повышения эффективности вы можете избежать создания временногоvector, так

void foo(std::vector<char> &data, std::stringstream &stream) {
    const std::string& str = stream.str();
    data.assign( str.begin(), str.end() );
}

Вы также можете избежатьstd::string используяstd::istreambuf_iterators:

void foo(std::vector<char> &data, std::stringstream &stream) {
    data.assign(
        std::istreambuf_iterator<char>( stream ), std::istreambuf_iterator<char>()
    );
}

но учитывая, что этоinput итераторы,vector не имеет возможности узнать, сколько данных будет назначено и может работать немного хуже, поскольку не можетreserve достаточно места, чтобы избежать перераспределения.

 ildjarn30 мая 2012 г., 22:07
Да, кроме указания типа символа. : -]
 ildjarn30 мая 2012 г., 22:04
Думаю, ты бы хотелistreambuf_iterator здесь, а неistream_iterator, иначе вы потеряете пробел.
 K-ballo30 мая 2012 г., 22:05
@ ildjarn: Спасибо, отредактированный ответ. Теперь это правильно?
 K-ballo30 мая 2012 г., 22:08
@ ildjarn: это нужно? Ссылка, которую я проверяю, говорит, что по умолчанию этоchar. Действительно, это необходимо. Похоже на причуду в этой конкретной ссылке.
 Xeo30 мая 2012 г., 22:16
О, кстаistreambuf_iterator могут быть построены из потоков, нет необходимости в вызовеrdbuf. :)

std::istream src;
std::vector<char> dst;

std::copy(std::istream_iterator<char>(src), std::istream_iterator<char>(), std::back_inserter(dst));

Istream_iterator использует форматированное преобразование (то есть пропускает пробелы), поэтому это может быть не то, что вам нужно. Я не уверен, какова ваша цель.

Ваш ответ на вопрос