Раствор 1
template<typename Fwd, typename Out, typename Operation>
Fwd move_if(Fwd first, Fwd last, Out result, Operation op)
{
Fwd swap_pos = first;
for( ; first != last; ++first ) {
if( !op(*first) ) *swap_pos++ = *first;
else *result++ = *first;
}
return swap_pos;
}
Идея проста. Что вы хотите сделать, это удалить элементы из одного контейнера и поместить их в другой, если предикат верен. Итак, возьмите код алгоритма std::remove()
, который уже выполняет удаление, и адаптируйте его к вашим дополнительным потребностям. В приведенном выше коде я добавил строку else
для копирования элемента, когда предикат имеет значение true.
Обратите внимание, что поскольку мы используем код std::remove()
, алгоритм фактически не сжимает входной контейнер. Тем не менее, он возвращает обновленный конечный итератор входного контейнера, так что вы можете просто использовать его и игнорировать дополнительные элементы. Используйте идиому erase-remove , если вы действительно хотите уменьшить контейнер ввода.
Решение 2
template<typename Bidi, typename Out, typename Operation>
Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
{
Bidi new_end = partition(first, last, not1(op));
copy(new_end, last, result);
return new_end;
}
Второй подход использует STL для реализации алгоритма. Лично я нахожу его более читабельным, чем первое решение, но у него есть два недостатка: во-первых, он требует более мощных двунаправленных итераторов для контейнера ввода, а не прямых итераторов, которые мы использовали в первом решении. Во-вторых, и это может или не может быть проблемой для вас, контейнеры не гарантированно имеют тот же порядок, что и до вызова std::partition()
. Если вы хотите сохранить порядок, замените этот вызов на std::stable_partition()
. std::stable_partition()
может быть немного медленнее, но имеет ту же сложность во время выполнения, что и std::partition()
.
Любой путь: вызов функции
list<int>::iterator p = move_if(l1.begin(), l1.end(),
back_inserter(l2),
bind2nd(less<int>(), 3));
Заключительные замечания
При написании кода я столкнулся с дилеммой: что должен возвращать алгоритм move_if()
? С одной стороны, алгоритм должен возвращать итератор, указывающий на новую конечную позицию входного контейнера, чтобы вызывающий мог использовать идиому удаления-удаления, чтобы уменьшить контейнер. Но с другой стороны, алгоритм должен возвращать позицию конца контейнера результата, потому что в противном случае вызывающий может найти его дорого. В первом решении итератор result
указывает на эту позицию, когда алгоритм заканчивается, тогда как во втором решении именно итератор, возвращаемый std::copy()
, указывает на эту позицию. Я мог бы вернуть пару итераторов, но для простоты я просто вернул один из итераторов.