Впрочем, некоторые склонны относить эти аргументы к стилю программирования, а вопросы стиля вызывают у программистов такую же жаркую полемику, как и тема выбора Лучшего В Мире Редактора (хотя о чем тут спорить? Всем известно, что это Emacs
). Было бы неплохо иметь более универсальный критерий для сравнения интервальных функций с одноэлементными. Для стандартных последовательных контейнеров такой критерий существует: эффективность. При работе со стандартными последовательными контейнерами применение одноэлементных функций приводит к более частому выделению памяти, более частому копированию объектов и/или выполнению лишних операций по сравнению с реализацией, основанной на интервальных функциях.
Предположим, вы хотите скопировать массив int
в начало vector
(исходное размещение данных в массиве может объясняться тем, что данные были получены через унаследованный интерфейс с языком С. Проблемы, возникающие при объединении контейнеров STL с интерфейсом С, описаны в совете 16). Решение с интервальной функцией insert
контейнера vector
выглядит просто и бесхитростно:
int data[numValues];// Предполагается, что numValues
// определяется в другом месте
vector
v.insert(v.begin().data,data+numValues): // Вставить int из data
// в начало v
Вероятно, решение с циклическим вызовом insert
выглядит примерно так:
vector
for(int i=0;i
insertLoc = v.insert(insertLoc.data[i]);
}
Обратите внимание на сохранение значения, возвращаемого при вызове insert
, до следующей итерации. Если бы значение insertLoc
не обновлялось после каждой вставки, возникли бы две проблемы. Во-первых, все итерации цикла после первой повели бы себя непредсказуемым образом, поскольку в результате каждого вызова insert
значение insertLoc
становилось бы недействительным. Во-вторых, даже если бы значение insertLoc
оставалось действительным, вставка всегда производилась бы в начале вектора (то есть в v.begin()
), и в результате содержимое массива было бы скопировано в обратном порядке.
Попробуем последовать совету 43 и заменим цикл вызовом сору:
copy(data.data+numValues.inserter(v.v.begin()));
После создания экземпляра шаблона решение с сору
практически идентично решению с циклом, поэтому в своем анализе эффективности мы ограничимся вторым вариантом и будем помнить, что все сказанное в равной степени относится к решению с сору. В случае с циклом вам будет проще понять, чем обусловлены потери эффективности. Да, это именно «потери» во множественном числе, поскольку решение с одноэлементной версией insert сопряжено с тремя видами затрат, отсутствующими при использовании интервальной версии insert.
Первая потеря обусловлена лишними вызовами функций. Естественно, последовательная вставка numValues элементов требует numValues вызовов insert. При вызове интервальной формы insert достаточно одного вызова функции, тем самым экономится numValues-1 вызов. Возможно, подстановка (inlining) избавит вас от этих затрат... а может, и нет. Уверенным можно быть лишь в одном: при использовании интервальной формы insert эти затраты заведомо отсутствуют.