Время от времени мне нужно перебирать подмножество элементов контейнера или просто извлекать их и пренебрегать остальными. Я заканчиваю тем, что использую boost::range::adaptors::filtered
создать эту ленивую коллекцию.
for(auto&& i : container | filtered(predicate)) {
// .. do stuff
}
Есть ли причина отсутствия алгоритма сбора (как в сборе Руби) в STL (у нас есть только copy_if, который не совпадает)? Или какая-либо причина против использования этого?
Возможная реализация может быть:
template<class Container, class Predicate>
Container collect(Container&& c, Predicate&& p) {
Container n;
for(auto&& i : c) {
if(p(i)) {
n.push_back(i);
}
}
return n;
}
но lazy_collect
также может быть полезно, чтобы избежать копирования.
Все ответы ниже великолепны. Я хотел бы отметить их всех. Я не знал о std::back_inserter
, Собирать вещи теперь так же просто, как:
boost::copy( orig | filtered(predicate), std::back_inserter(collection));
Стандартные алгоритмы не работают напрямую с контейнерами (создавая или уничтожая их или изменяя их размеры). Они работают с диапазонами итераторов, и единственное изменение, которое вносит алгоритм, — это назначение через итераторы.
Итак, предложенная вами операция полностью выходит за рамки стандартных алгоритмов. Возможно, в стандарте «должен» быть большой дополнительный набор общих контейнерных операций, включая эту, но «философия STL» заключается в работе с итераторами.
Не ленивая операция, которую вы предлагаете, может быть выполнена с помощью std::back_inserter
а также std::copy_if
(опционально с помощью итераторов перемещения) или move_if
(если вы катите это самостоятельно). std::copy_if
отсутствовал в C ++ 03, но это был случайный недосмотр.
Ленивая версия не может быть сделана простым соединением стандартных компонентов — нет чистого способа объединить «выходные данные» одного алгоритма прямо во «входные данные» другого алгоритма без промежуточного хранения. Вот почему Boost предоставляет такое интересное разнообразие итераторов, как и диапазоны.
Я не знаю, почему они не были включены в C ++ 11, но одна вещь, которую стоит отметить в C ++ 11, это то, что было довольно поздно. Предложения были отклонены из-за нехватки времени, поэтому причина может быть такой простой, как «это никогда не считалось достаточно важным, чтобы сделать предложение, учитывая известную существующую рабочую нагрузку».
Что касается вашей конкретной реализации, не все контейнеры имеют push_back
и поэтому вы, вероятно, не должны использовать Container
в качестве имени параметра шаблона. PushBackSequence
будет более описательным из требований.
Как насчет этого:
#include <algorithm>
#include <iterator>
std::copy_if(container.begin(), container.end(), std::back_inserter(result), []{...})
куда container
это контейнер, из которого вы хотите забрать, и result
это контейнер, в котором будет храниться коллекция
Из комментариев:
При использовании copy_if вам нужно заранее знать, сколько места вам понадобится
Это не правда. Ты можешь использовать std::copy_if
с задняя вставка:
#include <algorithm> // For std::copy_if
#include <iterator> // For std::back_inserter
#include <vector>
#include <iostream>
int main()
{
std::vector<int> v(10);
std::iota(begin(v), end(v), 0); // Fills the vector with 0..9
std::vector<int> out;
std::copy_if(begin(v), end(v), back_inserter(out), [] (int x)
// ^^^^^^^^^^^^^^^^^^
{
return x > 4;
});
for (auto x : out) { std::cout << x << " "; }
}
Вот живой пример.
И если вам нужна функция, которая работает непосредственно с контейнером, а не с диапазоном, определенным парой итераторов, вы можете написать следующий помощник:
template<typename C1, typename C2, typename P>
void cont_copy_if(C1&& src, C2&& dst, P&& p)
{
std::copy_if(
begin(std::forward<C1>(src)),
end(std::forward<C1>(src)),
back_inserter(std::forward<C2>(dst)),
std::forward<P>(p)
);
}
Какой бы вы просто использовали этот способ:
int main()
{
std::vector<int> v(10);
std::iota(begin(v), end(v), 0);
std::list<int> out;
// ^^^^^^^^^
// You could use a different destination container
cont_copy_if(v, out, [] (int x) { return x > 4; });
// ^^^^^^^^^^^^
for (auto x : out) { std::cout << x << " "; }
}
И конечно живой пример.