using namespace std;
template
Out copyIf(In first, In last, Out result, UnPred pred) {
for ( ; first != last; ++first)
if (pred(*first)) *results = *first;
return(result);
}
int main() {
cout << "Введите несколько строк: ";
istream_iterator
istream_iterator
vector
list
copyIf(v.begin(), v.end(), back_inserter
>(lst),
bind2nd(less
printContainer(lst);
}
Запуск примера 7.10 будет выглядеть примерно так.
Введите несколько строк: apple banana danish eclaire
^Z
-----
apple banana
Вы видите, что он копирует в результирующий диапазон только те значения, которые меньше, чем «cookie».
Стандартная библиотека содержит шаблон функции сору
copy_if
), так что пример 7.10 делает именно это. Его поведение довольно просто: при наличии диапазона-источника и начала диапазона-приемника производится копирование в целевой диапазон элементов, для которых унарный функтор-предикат возвращает true
.Этот алгоритм прост, но в его реализации есть еще кое-что, что привлекает внимание. Посмотрев на объявление, вы увидите, что в нем присутствует три параметра шаблона.
template
Out copyIf(In first, In last, Out result UnPred pred) {
Первый параметр шаблона In
copyIf
, — это извлечь разыменованное значение этого итератора и перевести итератор на следующий элемент. Это дает описание категории итератора ввода (категории итераторов описаны в рецепте 7.1), так что с помощью указания имени параметра шаблона In
мы объявляем именно этот тип итератора. Стандартного соглашения здесь нет (In
и Out
— это мои соглашения, которые я описал в первом рецепте этой главы), но вы легко можете придумать свои собственные соглашения об именах: InIter
, Input_T
или даже InputIterator.
Второй параметр шаблона Out
— это тип итератора, который указывает на диапазон, в который будут копироваться элементы, copyIf
должен иметь возможность записать разыменованное значение в выходной итератор и увеличить его значение, что дает нам описание оператора вывода. Объявив требования к итераторам с помощью имен параметров шаблона, вы делаете соглашения о вызовах алгоритма понятными без документации. Но зачем использовать две разные категории итераторов?Имеется, по крайней мере, две причины использования в copyIf
vector
и записываю в list
.vector
list
copyIf(v.begin(), v.end(), back_inserter
>(lst),
bind2nd(less
Если попробовать сделать то же самое, использовав в алгоритме один и тот же тип итераторов, то он просто не скомпилируется.
В примере 7.10 я в качестве начала выходного диапазона передаю back_inserter
lst.begin
. Это делается потому, что lst не содержит элементов, и в этом алгоритме (как и в стандартном алгоритме копирования) целевой диапазон должен быть достаточно большим, чтобы вместить все элементы, которые будут в него скопированы. В противном случае увеличение итератора вывода в copyIf
приведет к неопределенному поведению. back_inserter
возвращает итератор вывода, который при его увеличении вызывает для контейнера метод push_back
. В результате этого при каждом увеличении выходного итератора размер lst
увеличивается на один. Более подробно шаблон класса back_inserter
я описываю в рецепте 7.5.