Отфильтрованный адаптер с диапазоном усиления в отрицательном диапазоне

Можно / можно ли отрицать адаптер с повышенным фильтрованием, например

std::vector<int> v = {1, 2, 3, 4, 5};
for(auto i : v | !filtered(is_even))
    std::cout << i << std::endl; // prints 1,3,5

вместо того, чтобы делать отрицание внутри лямбда-выражения?

Мотивация: я много работаю с фильтрованными и лямбда-функциями, однако, когда я использую фильтр более одного раза, я обычно превращаю его в специальный фильтр, например

for(auto i : v | even) // note: my filters are more complex than even.
    std::cout << i << std::endl; // prints 2,4

Прямо сейчас, когда мне нужно отрицание, я создаю специальный фильтр для них, например,

for(auto i : v | not_even)
    std::cout << i << std::endl; // prints 1,2,3

но было бы лучше, если бы можно было просто отменить фильтр, например,

for(auto i : v | !even)
    std::cout << i << std::endl; // prints 1,2,3

1 ответ

Решение

Вот что я придумал в короткие сроки:

#include <boost/range/adaptors.hpp>
#include <boost/functional.hpp>
#include <iostream>

namespace boost { 
    namespace range_detail { 

        template <typename T>
            auto operator!(filter_holder<T> const& f) -> decltype(adaptors::filtered(boost::not1(f.val)))
            {
                return adaptors::filtered(boost::not1(f.val));
            }
    }
}

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    std::function<bool(int)> ll = [](int i){return 0 == (i%2);}; // WORKS
    // bool(*ll)(int) = [](int i){return 0 == (i%2);};           // WORKS
    // auto ll = [](int i){return 0 == (i%2);};                  // not yet

    auto even = filtered(ll);

    for (auto i : v | !even)
    {
        std::cout << i << '\n';
    }
}

Смотрите это в прямом эфире на http://liveworkspace.org/code/IDDkJ%245

Обратите внимание, что в настоящее время он обрабатывает предикаты вида function pointer а также std::function<...>, но пока не голые лямбды (на GCC 4.7.2)

Это не дает точного ответа на вопрос, поскольку не отменяет фильтр, а только предикат. Я все еще пишу это, потому что поиск решения поставил этот вопрос в качестве первого результата.

По сравнению с другим ответом это имеет то преимущество, что нам не нужно добавлять собственный код в namespace boost::range_detail,

Решение C++17

Функция std::not_fn может использоваться для создания отрицательного предиката.

#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>

struct is_even
{
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    for( auto i : v | filtered( std::not_fn( is_even{} ) ) )
    {
        std::cout << i << ' ';
    }
}

Live Demo

Решение C++11, C++14

Функция std::not1 может использоваться для создания отрицательного предиката. У него есть дополнительное требование, что предикат должен определять тип члена, argument_type который имеет тот же тип, что и предикаты operator() параметр.

#include <boost/range/adaptors.hpp>
#include <functional>
#include <iostream>

struct is_even
{
    using argument_type = int;
    bool operator()( int x ) const { return x % 2 == 0; }
};

int main()
{
    using namespace boost::adaptors;
    int const v[] = { 1, 2, 3, 4 };

    for( auto i : v | filtered( std::not1( is_even{} ) ) )
    {
        std::cout << i << ' ';
    }
}

Live Demo

Другие вопросы по тегам