results.reserve(results.size+values.size); // Обеспечить наличие
// в векторе results
// емкости для value.size
// элементов
transform(values.begin, values.end, // То же, что и ранее,
inserter(results, results.begin+results.size/2), // но без лишних
transmogrify); // перераспределений памяти
При использовании функции reserve
для повышения эффективности серии вставок всегда помните, что reserve
увеличивает только reserve
при работе с алгоритмом, который должен включать новые элементы в vector
или string
, необходимо использовать итератор вставки (то есть итератор, возвращаемый при вызове back_inserter
, front_inserter
или inserter
).
Чтобы это стало абсолютно ясно, рассмотрим values
к results
):
vector
vector
…
results.reserve(results.size+values.size); // См. ранее
transform(values.begin, values.end, // Результаты вызова
results.end, // transmogrify записываются
transmogrify); // в неинициализированную
// память; последствия
// непредсказуемы!
В этом фрагменте transform
в блаженном неведении пытается выполнить присваивание в неинициализированной памяти за последним элементом results
. Обычно подобные попытки приводят к ошибкам времени выполнения, поскольку операция присваивания имеет смысл лишь для двух объектов, но не между объектом и двоичным блоком с неизвестным содержимым. Но даже если этот код каким-то образом справится с задачей, вектор results
не будет знать о новых «объектах», якобы созданных в его неиспользуемой памяти. С точки зрения results
вектор после вызова transform
сохраняет прежний размер, а его конечный итератор будет указывать на ту же позицию, на которую он указывал до вызова transform
. Мораль? Использование reserve
без итератора вставки приводит к непредсказуемым последствиям внутри алгоритмов и нарушению целостности данных в контейнере.
В правильном решении функция reserve
используется в сочетании с итератором вставки:
vector
vector
…
results.reserve(results.size+values.size); // См. ранее
transform(values.begin, values.end, // Результаты вызова
back_inserter(results), // transmogrify записываются
transmogrify); // в конец вектора results
// без лишних перераспределений
// памяти
До настоящего момента предполагалось, что алгоритмы (такие как transform
) записывают результаты своей работы в контейнер в виде новых элементов. Эта ситуация является наиболее распространенной, но иногда новые данные требуется записать поверх существующих. В таких случаях итератор вставки не нужен, но вы должны в соответствии с данным советом проследить за тем, чтобы приемный интервал был достаточно велик.
Допустим, вызов transform
должен записывать результаты в results
поверх существующих элементов. Если количество элементов в results
не меньше их количества в values
, задача решается просто. В противном случае придется либо воспользоваться функцией resize
для приведения results
к нужному размеру:
vector
…
if (results.size < values.size) { // Убедиться в том, что размер
results.resize(values.size); // results по крайней мере
} // не меньше размера values
transform(values,begin, values.end, // Перезаписать первые
back_inserter(results), // values.size элементов results