Как эмулировать remove_unless
У меня есть код, чтобы удалить все элементы из std::vector<int>
которые меньше чем некоторые int limit
, Я написал несколько функций, которые частично применяют лямбда-выражения:
auto less_than_limit = [](int limit) {
return [=](int elem) {
return limit > elem;
};
};
auto less_than_three = less_than_limit(3);
Когда я проверяю это с std::vector<int> v{1,2,3,4,5};
Я получаю ожидаемые результаты:
for(auto e: v) {
std::cout << less_than_three(e) << " ";
}
// 1 1 0 0 0
Я могу легко удалить все элементы меньше трех:
auto remove_less_than_three = std::remove_if(std::begin(v), std::end(v), less_than_three);
v.erase(remove_less_than_three, v.end());
for(auto e: v) {
std::cout << e << " ";
}
// 3 4 5
Как бы удалить элементы больше или равные 3, используя less_than_three
?
Я пробовал оборачивать less_than_three
в std::not1
, но получил ошибки:
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>'
class unary_negate
^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'struct main()::<lambda(int)>::<lambda(int)>'
operator()(const typename _Predicate::argument_type& __x) const
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<main()::<lambda(int)>::<lambda(int)> >) (int&)'
{ return bool(_M_pred(*__it)); }
^
Я тогда попробовал std::not1(std::ref(less_than_three))
, но получил эти ошибки:
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:742:11: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >'
class unary_negate
^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/stl_function.h:755:7: error: no type named 'argument_type' in 'class std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> >'
operator()(const typename _Predicate::argument_type& __x) const
^
/usr/local/Cellar/gcc/5.3.0/include/c++/5.3.0/bits/predefined_ops.h:234:30: error: no match for call to '(std::unary_negate<std::reference_wrapper<main()::<lambda(int)>::<lambda(int)> > >) (int&)'
{ return bool(_M_pred(*__it)); }
^
Как я могу отменить функцию в std::remove_if
не меняя логику моей лямбды? Другими словами, как я могу подражать remove_unless
?
4 ответа
std::not1
предполагает, что ваш функциональный объект будет производным от std::unary_function
или, по крайней мере, предоставить тот же интерфейс, так что он будет иметь typedefs для result_type
а также argument_type
,
Поскольку лямбда не будет определять их, вы не сможете использовать not1
на них.
Очевидным выбором будет создание чего-то похожего на not1
самостоятельно, но используя более современные методы, чтобы обнаружить / передать тип аргумента / результата из того, что он изменяет.
Если вы действительно хотите использовать not1
тогда самым разумным подходом было бы сделать сравнение с std::less
а также std::bind
чтобы указать значение, с которым вы собираетесь сравнивать (т. е. это в основном C++03, поэтому, если вы собираетесь его использовать, вы пишете все в стиле C++03).
not1
несколько устарел (и требует, чтобы функтор предоставлял определенные определения типов членов, чего явно не имеет лямбда).
Вы должны будете написать отрицатель самостоятельно:
auto negate = [] (auto&& f) {return [f=std::forward<decltype(f)>(f)]
(auto&&... args) {return !f(std::forward<decltype(args)>(args)...);};};
Вы также можете определить тип возврата лямбда с std::function.
auto remove_gte_three = std::remove_if(std::begin(v), std::end(v), std::not1(std::function<int(int)>(less_than_three)));
Старый способ с not1() отрицателем (C++11):
// You apply the not1() negator adapter
// to the result of less_than_three() like this:
std::function<bool(int)> f = less_than_three;
auto it = remove_if(begin(v), end(v), not1(f));
Новый способ с лямбда (C++14):
// Or with lambda you can negate an unary predicate.
// Thanks to Stephan T. Lavavej
template <typename T, typename Predicate>
void keep_if(std::vector<T>& v, Predicate pred)
{
auto notpred = [&pred](const T& t) { return !pred(t); };
v.erase(remove_if(v.begin(), v.end(), notpred), v.end());
}
Использование:
keep_if(v, less_than_three);
Или более общее решение (C++14):
template <ForwardIterator I, Predicate P>
I remove_if_not(I first, I last, P pred)
{
return std::remove_if(first, last,
[&](const ValueType(I)& x){ return !pred(x); });
}
Использование:
auto p = remove_if_not(begin(v), end(v), less_than_three);
v.erase(p, v.end());
// Result: 1 2