Допуская подобную ошибку, программист почти всегда рассчитывает на то, что результаты вызова алгоритма будут вставлены в приемный контейнер вызовом insert
. Если вы хотите, чтобы это произошло, так и скажите. В конце концов, STL — всего лишь библиотека, и читать мысли ей не положено. В нашем примере задача решается построением итератора, определяющего начало приемного интервала, вызовом back_inserter
:
vector values;
transform(values.begin(), // Применить transmogrify к каждому
values.end(), // объекту вектора values
back_inserter(results), // и дописать значения в конец results
transmogrify);
При использовании итератора, возвращаемого при вызове back_inserter
, вызывается push_back
, поэтому back_inserter
может использоваться со всеми контейнерами, поддерживающими push_back
(то есть со всеми стандартными последовательными контейнерами: vector
, string
, deque
и list
). Если вы предпочитаете, чтобы алгоритм вставлял элементы в начало контейнера, Воспользуйтесь front_inserter
. Во внутренней реализации front_inserter
используется push_front
, поэтому front_inserter
работает только с контейнерами, поддерживающими эту функцию (то есть deque
и list
).
… //См. ранее
listresults; // Теперь используется
// контейнер list
transform(values.begin(), values.end(), // Результаты вызова transform
front_inserter(results), // вставляются в начало results
transmogrify); // в обратном порядке
Поскольку при использовании front_inserter
новые элементы заносятся в начало results
функцией push_front
, порядок следования объектов в results
будет обратным по отношению к порядку соответствующих объектов в values
. Это ишь одна из причин, по которым front_inserter
используется реже back_inserter
. Другая причина заключается в том, что vector
не поддерживает push_front
, поэтому front_inserter
не может использоваться с vector
.
Чтобы результаты transform
выводились в начале results
, но с сохранением порядка следования элементов, достаточно перебрать содержимое values
в обратном порядке:
list results; // См. ранее
…
transform(values. rbegin(), values. rend(), // Результаты вызова transform
front_inserter(results), // вставляются в начало results
transmogrify); // с сохранением исходного порядка
Итак, front_inserter
заставляет алгоритмы вставлять результаты своей работы в начало контейнера, a back_inserter
обеспечивает вставку в конец контейнера. Вполне логично предположить, что inserter
заставляет алгоритм выводить свои результаты с произвольной позиции:
vector values; // См. ранее
…
vector results; // См. ранее - за исключением того, что
… // results на этот раз содержит данные
// перед вызовом transform.
transform(values.begin(), // Результаты вызова transmogrify
values.end(), // выводятся в середине results
inserter(results, results, begin()+results.size()/2),
transmogrify);
Независимо от выбранной формы — back_inserter, front_inserter
или inserter
— объекты вставляются в приемный интервал по одному. Как объясняется в совете 5, это может привести к значительным затратам для блоковых контейнеров ( vector
, string
и deque
), однако средство, предложенное в совете 5 (интервальные функции), неприменимо в том случае, если вставка выполняется алгоритмом. В нашем примере transform
записывает результаты в приемный интервал по одному элементу, и с этим ничего не поделаешь.
При вставке в контейнеры vector
и string
для сокращения затрат можно последовать совету 14 и заранее вызвать reserve
. Затраты на сдвиг элементов при каждой вставке от этого не исчезнут, но по крайней мере вы избавитесь от необходимости перераспределения памяти контейнера:
vector values; // См. Ранее
vector results;
…
results. reserve(results.size()+values.size()); // Обеспечить наличие
// в векторе results
// емкости для value.size()
// элементов
transform(values.begin(), values.end(), // То же, что и ранее,
inserter(results, results.begin()+results.size()/2), // но без лишних
transmogrify); // перераспределений памяти
Читать дальше