Написание собственного фильтра для диапазона
Пытаюсь разобраться, как работать с диапазонами в С++20 по книге Rainer Grimm "C++20 Get the details".
Хочу написать собственный фильтр, которому бы можно было передать контейнер (пусть просто вектор) и который бы отфильтрованные элементы выводил бы в него и передавал дальше. Чтоб пожно было написать что-то вроде
vect | views::filter(f1) | views::transform(f2) | copy_to(w) | views::filter(f3)
примерно так. Т.е. после фильтрации и преобразования скидываем в вектор w и фильтруем себе дальше.
Только вот совершенно запутался, как сделать такую вещь. Если я правильно понимаю, то должны быть только begin() и end() для диапазона. Так? А как получить очередной элемент и записать его?
Если где-то есть более ПРОСТОЕ описание написания фильтров (да и самих диапазонов... никак не могу их понять, как оно там внутри) - буду рад услышать.
Свой позор привожу исключительно для того, чтоб вопрос не закрыли из-за отсутствия собственных попыток. Потому что это в стиле "лишь бы компилировалось" из-за полного непонимания, как это должно работать...
using namespace std::ranges;
template<typename Container, std::ranges::input_range Range> requires std::ranges::view<Range>
class copy_to : public std::ranges::view_interface<copy_to<Container,Range>>
{
private:
Container * c = nullptr;
Range range_{};
std::ranges::iterator_t<Range> begin_{ std::begin(range_) };
std::ranges::iterator_t<Range> end_{ std::end(range_) };
public:
//Container() = default;
constexpr copy_to(Container& c, Range r): c(&c), range_(std::move(r)),
begin_(std::begin(r)), end_(std::end(r))
{
for(auto b = begin_; b != end_; ++b) c->push_back(*b);
}
constexpr copy_to(Container& c): c(&c), begin_(std::begin(c)), end_(std::end(c))
{
for(auto b = begin_; b != end_; ++b) c->push_back(*b);
}
constexpr auto begin() const {
return begin_;
}
constexpr auto end() const {
return end_;
}
};
template<typename Container, typename Range>
copy_to(Container&, Range&&) -> copy_to<Container, std::ranges::views::all_t<Range>>;
Ответы (1 шт):
Если вы пишите свою операцию для работы с диапазонами, то вы имеете дело с тремя типами: (1) - результат выполнения copy_to(w) - это может быть собственно тип copy_to. Этот тип не обязан выглядеть как диапазон или контейнер. (2) - результат выполнения ...|copy_to(w) - это уже другой тип. И он, как раз, обязан быть контейнером или диапазоном. (3) - результат выполнения ...|copy_to(w)|... - это уже забота операнда справа, если только мы не переопределим оператор | (справо), например, для оптимизации.
Шаблон std::ranges::view_interface предназначен для определения собственных диапазонов, т.е. он может подойти для типа (2), но ненужен для (1). Вы попытались соединить в одном типы (1) и (2).
Пример решения:
#include <ranges>
#include <vector>
#include <iostream>
using namespace std::ranges;
template<typename Container>
class copy_to
{
Container& output;
public:
copy_to(Container& output) : output(output) {}
copy_to(const copy_to&) = default;
copy_to(copy_to&&) = default;
template<viewable_range Range>
friend auto operator | ( Range range, const copy_to& output )
{
auto& w = output.output;
for( auto i : range )
w.push_back( i );
return range;
}
};
template<typename Container>
copy_to(Container&) -> copy_to<Container>;
int main()
{
auto f1=[](int i){ return i%3!=0; };
auto f2=[](int i){ return i+2; };
auto f3=[](int i){ return i*3; };
std::vector<int> vect = {1,2,3};
std::vector<int> w;
for( int i : vect | views::filter(f1) | views::transform(f2) | copy_to(w) | views::transform(f3) )
std::cout<<i<<" ";
std::cout<<std::endl;
for( int i : w )
std::cout<<i<<" ";
std::cout<<std::endl;
}