Как эффективно скопировать std::string в вектор

У меня есть строка

std::string s = "Stack Overflow";

Это мне нужно скопировать в вектор. Вот как я это делаю

std::vector<char> v;
v.reserve(s.length()+1);
for(std::string::const_iterator it = s.begin(); it != s.end(); ++it)
{
    v.push_back( *it );
}
v.push_back( '\0' );

Но я слышал, что операции на полигоне более эффективны. Вот я и думаю о чем-то подобном

std::vector<char> v( s.begin(), s.end());
v.push_back('\0');

Но лучше ли это в данном случае? А как насчет потенциального перераспределения при вставке '\0'?
Еще один подход, который я думаю, заключается в следующем

std::vector<char> v(s.length()+1);
std::strcpy(&v[0],s.c_str());

Возможно быстро, но потенциально небезопасно?
ИЗМЕНИТЬ
Должна быть строка с завершающим нулем, которую можно использовать (чтение/запись) внутри функции C


person molita    schedule 11.09.2011    source источник
comment
Я бы использовал последний с strncopy. Но это только мое мнение.   -  person Nobody moving away from SE    schedule 11.09.2011
comment
Откуда струна? Как используется полученный вектор? Нужно ли включать завершающий 0? Можете ли вы просто использовать строку в качестве контейнера вместо того, чтобы вообще нуждаться в векторе?   -  person tenfour    schedule 11.09.2011
comment
@tenfour Результирующий вектор должен быть передан функции c неконстантным способом, т.е. чтение/запись   -  person molita    schedule 11.09.2011
comment
Это настолько неправильно, что никому не пришло в голову изобрести интерфейс, который бы делал это плавно   -  person Puppy    schedule 11.09.2011
comment
@DeadMG: по моему опыту, интерфейс, который сделает это гладко, то есть оболочка C ++ вокруг той или иной библиотеки C, - это то, где такая конструкция больше всего нужна.   -  person Steve Jessop    schedule 11.09.2011


Ответы (3)


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

std::vector<char> v(s.c_str(), s.c_str() + s.length() + 1);

Поскольку c_str() возвращает строку с завершающим нулем, вы можете просто скопировать ее целиком в вектор.

Однако на самом деле я не уверен, насколько оптимизирован этот конструктор. Я действительно знаю, что std::copy оптимизирован настолько, насколько это возможно, поэтому, возможно (измерьте!) следующее быстрее:

std::vector<char> v(s.length() + 1);
std::copy(s.c_str(), s.c_str() + s.length() + 1, v.begin());

Если функция C не изменяет строку, просто передайте c_str() напрямую и отбросьте константность. Это безопасно, пока функция C читает только строку.

person Konrad Rudolph    schedule 11.09.2011
comment
Я знаю, что std::copy оптимизирован настолько, насколько это возможно, или если есть сомнения относительно QOI конкретной стандартной библиотеки шаблонов, то memcpy подойдет и имеет еще больше шансов быть дьявольски оптимизированным. - person Steve Jessop; 11.09.2011
comment
@ Стив Угу. Джек! Не нравятся низкоуровневые функции C, где алгоритмы C++ концептуально намного чище и принципиально способны оптимизировать больше благодаря информации о типах. Нет ни одного случая, когда memcpy должен работать лучше (за исключением пункта о перекрытии памяти). - person Konrad Rudolph; 11.09.2011
comment
@Konrad: Я полагаю, никогда не должно быть сомнений в QOI стандарта std::copy, поскольку вы можете просто посмотреть код и / или профилировать его, чтобы убедиться. Я думаю, что std::copy настолько оптимизирован, насколько это возможно, это либо утверждение того, кто вручную проверил каждую реализацию C++ в мире, либо предположение; довольно тривиальная реализация std::copy, не специализированная для char, и оптимизатор, который не может компенсировать это. В этом случае я продолжаю проверять, лучше ли memcpy. - person Steve Jessop; 11.09.2011
comment
Резюмируя, я не говорю о том, std::copy следует ли оптимизироваться (конечно, должно - должно быть все). Я просто отмечаю, что попробовать дальше, если это не так. - person Steve Jessop; 11.09.2011
comment
@Steve Что ж, ограничивая утверждение основными поставщиками компиляторов / библиотек (GCC, ICC, VC ++), я действительно могу с уверенностью заявить об этом. Как только вы отважитесь на эзотерические компиляторы/платформы, все ставки в целом отменяются. Однако многие современные библиотеки и приложения просто полагаются на QOI алгоритмов стандартной библиотеки. - person Konrad Rudolph; 11.09.2011
comment
@Konrad: ну, если вы собираетесь начать делать реалистичные предположения, то мы, вероятно, можем просто использовать тот факт, что хранилище строк является непрерывным и завершается нулем. Передайте &s[0] загадочной функции C, и будьте уверены, что если эта функция угрожает сделать строку еще короче, вы resize() впоследствии дойдете до позиции первого нулевого символа. Сохраняет любые копии. - person Steve Jessop; 11.09.2011
comment
@Konrad Rudolph Если вектор предварительно объявлен, то есть у вас нет возможности инициализировать его с помощью s.length()+1, вы бы вызвали v.resize() или v.reserve() или оба перед вызовом std::copy? - person molita; 11.09.2011
comment
@molita: позвони resize. Вам не разрешен доступ к зарезервированным индексам, размер которых превышает текущий размер вектора. - person Steve Jessop; 11.09.2011
comment
Я не понимаю, почему вы должны сомневаться в эффективности конструктора диапазона итератора. Во втором случае вы сначала инициализируете все это значением '\0' (снова используя сомнительную реализацию векторного конструктора), почему это должно быть лучше? - person UncleBens; 11.09.2011
comment
@UncleBens Потому что я знаю, что std::copy чрезвычайно оптимизирован, особенно для этого случая. Я не знаю, есть ли конструктор vector. Конечно, обычно он должен просто использовать std::copy или что-то подобное (с перемещением, а не копированием) внутри. - person Konrad Rudolph; 11.09.2011
comment
С G++ все, что делает конструктор пары итераторов вектора, в конечном итоге вызывает std::copy. Если все знают, что это так хорошо, почему реализация библиотеки выбрала что-то хуже? - И вы все еще не можете доверять вектору для быстрого заполнения буфера нулями. - person UncleBens; 11.09.2011
comment
что вы хотите сделать с вектором символов? если вы хотите поместить его в (не изменяющую) функцию C, просто отбросьте const из s.c_str() ? - person Alexander Oh; 11.09.2011
comment
@Alex Алекс Ты сказал: без модификации. Это, кстати, то, что я сказал в своем ответе. Но как только функция C изменяет строковый буфер, вектор символов является лучшим переносимым решением. - person Konrad Rudolph; 11.09.2011

В большинстве случаев вам не нужен вектор char, так как std::string в значительной степени является контейнером char. std::string также имеют функции begin и end. И у него также есть функция c_str(), которая возвращает c-строку, которую вы можете передать любой функции, которая ожидает const char*, например:

void f(const char* str); //c-function

std::string s="some string";
f(s.c_str());

Итак, зачем вам std::vector<char>?

На мой взгляд, vector<char> очень-очень редкая потребность, но если она мне когда-нибудь понадобится, я, наверное, напишу это:

std::vector<char> v(s.begin(), s.end());

И для меня v.push_back('\0') не имеет особого смысла. Нет такого требования к вектору, чтобы последний элемент был '\0', если value_type равно char.

Хорошо, как вы сказали, std::string::c_str() возвращает const char*, а c-функция нуждается в неконстантном char* , тогда вы можете использовать std::vector, потому что хотите воспользоваться RAII, который реализует вектор:

void g(char* s); //c-function

std::vector<char> v(s.begin(), s.end());
s.push_back('\0');

g(&v[0]);

что мне кажется хорошо. Но RAII — это все, что вам нужно, тогда у вас есть и другой вариант:

{
  std::vector<char> memory(s.size()+1);
  char *str = &memory[0]; //gets the memory!
  std::strcpy(str, s.c_str());

  g(str);
  //....

} //<--- memory is destroyed here.

Используйте std::strcpy, std::memcpy или std::copy в зависимости от того, что быстрее, так как я не могу сказать, какой из них обязательно быстр, без профилирования.

person Nawaz    schedule 11.09.2011
comment
Как функция C распознает ее как строку с нулевым завершением? - person molita; 11.09.2011
comment
@molita: Вы не упомянули это требование. - person Puppy; 11.09.2011
comment
@molita: Вы можете просто передать s.c_str(). - person Nawaz; 11.09.2011
comment
Извините, -1. Вы проводите большую часть своего ответа, объясняя, что вектор не нужен. Затем вы признаете, что это может быть необходимо, но не продвигаетесь к тому, что уже есть в вопросе, а просто выражаете необоснованное предпочтение одной из двух возможностей. - person Steve Jessop; 11.09.2011
comment
@Steve: вопрос был отредактирован. Ранее он не упоминал, зачем ему это нужно. Кроме того, я не сказал the vector isn't necessary. Я сказал, что это очень редкая необходимость. - person Nawaz; 11.09.2011
comment
-1 удалено, поскольку вы предложили альтернативу, не упомянутую в вопросе. Я не думаю, что это должно иметь значение, зачем это нужно - комментарии предназначены для того, чтобы сообщить людям, что они не должны задавать вопрос, который они задают, а не ответы. - person Steve Jessop; 11.09.2011

Я не думаю, что std::strcpy(&v[0],s.c_str()); - хороший выбор. Я думаю, что c_str() разрешено перераспределять.

Если вам каким-то образом "нужен" \0 для работы с C-API, то полагайтесь на string::c_str(), который предоставит его вам по запросу. Не думайте, что вам нужно поместить его в вектор-символ, большинство вещей, которые вы можете сделать с самой строкой, как с вектором.

Обновление:

Если вы убедитесь, что ваш вектор инициализируется с помощью 0s, вы можете обойти вызов c_str, используя strncopy:

std::vector<char> v(s.length()+1, 0);  // added '0'
std::strncpy(&v[0],&s[0],s.length());  // no c_str()
person towi    schedule 11.09.2011
comment
Я согласен, что это не очень хороший выбор, но я не понимаю, насколько важно c_str() делать перераспределение. Код по-прежнему хорошо определен. - person Konrad Rudolph; 11.09.2011
comment
да, верно, четко определено. Я просто имел в виду, что он может захотеть предотвратить внутреннее перераспределение по соображениям производительности. Подождите, я обновляю свой ответ... - person towi; 11.09.2011
comment
Как построение вектора с 0 и затем копирование строки в него будет лучше, чем позволить конструктору вектора скопировать строку? - person Bo Persson; 11.09.2011
comment
@Bo: Нет, я бы не предпочел это решение. Я просто хотел указать путь strncpy. Я поставил +1 ответ Конрада :-) - person towi; 11.09.2011