在不知道参数类型的情况下对lambda求反
Negate a lambda without knowing the argument type?
我正在尝试编写一个与Python过滤器类似的就地过滤器函数。例如:
std::vector<int> x = {1, 2, 3, 4, 5};
filter_ip(x, [](const int& i) { return i >= 3; });
// x is now {3, 4, 5}
首先我尝试了这个:
template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
c.erase(std::remove_if(c.begin(), c.end(), std::not1(f)), c.end());
}
但是,这不起作用,因为lambdas没有argument_type
字段。
以下变体确实有效:
template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),
[&f](const typename Container::value_type& x) {
return !f(x);
}),
c.end());
}
然而,这似乎不太理想,因为以前它只要求Container
具有begin
、end
和erase
,而现在它还要求定义value_type
。另外,它看起来有点笨重。
这是答案中的第二种方法。第一个将使用std::not1(std::function<bool(const typename Container::value_type&)>(f))
而不是lambda,lambda仍然需要类型。
我还尝试将arg函数指定为具有已知参数类型的std::function
:
template <typename Container, typename Arg>
void filter_ip(Container& c, std::function<bool(const Arg&)>&& f)
{
c.erase(std::remove_if(c.begin(), c.end(), std::not1(f)), c.end());
}
但后来我得到了:
'main()::<lambda(const int&)>' is not derived from 'std::function<bool(const Arg&)>'
有办法解决这个问题吗?从直觉上看,它似乎应该非常简单,因为您所需要做的就是将一个not应用于您已经知道f
返回的bool。
如果不能使用C++14通用lambda,那么用模板化的operator()
:将其委托给一个经典函子怎么样
#include <utility>
#include <vector>
#include <algorithm>
#include <iostream>
template <class F>
struct negate {
negate(F&& f)
: _f(std::forward<F>(f)) {}
template <class... Args>
bool operator () (Args &&... args) {
return !_f(std::forward<Args>(args)...);
}
private:
F _f;
};
template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f)
{
c.erase(std::remove_if(
c.begin(),
c.end(),
negate<Filter>(std::forward<Filter>(f))),
c.end()
);
}
int main() {
std::vector<int> v {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
filter_ip(v, [](int i) {return bool(i%2);});
for(auto &&i : v)
std::cout << i << ' ';
std::cout << 'n';
}
输出:
1 3 5 7 9
在Coliru 上直播
template<class F>
struct not_f_t {
F f;
template<class...Ts>
decltype(!std::declval<typename std::result_of<F&(Ts...)>::type>())
operator()(Ts&&...ts) {
return !f(std::forward<Ts>(ts)...);
}
};
template<class F, class dF=typename std::decay<F>::type>
not_f_t<dF> not_f(F&& f){
return {std::forward<F>(f)};
}
或者在C++14中,我们可以省去not_f_t
类并执行:
template<class F,class dF=std::decay_t<F>>// dF optional
auto not_f(F&& f){
return [f=std::forward<F>(f)](auto&&...args)mutable
->decltype(!std::declval<std::result_of_t<dF&(decltype(args)...)>>()) // optional, adds sfinae
{
return !f(decltype(args)(args)...);
};
}
然后,因为它摇晃:
template<class C, class F>
void erase_remove_if( C&& c, F&& f ) {
using std::begin; using std::end;
c.erase( std::remove_if( begin(c), end(c), std::forward<F>(f) ), end(c) );
}
我们得到:
std::vector<int> x = {1, 2, 3, 4, 5};
erase_remove_if(x, not_f([](int i){return i>=3;}));
在我看来,如果您已经需要begin
、end
和erase
,那么还需要value_type
是一个非常小的添加。如果您可以摆脱对erase
的需求,那么您至少可以获得一些真正的容器,但消除对value_type
的需求并不能起到多大作用。
尽管如此,如果您有一个容器确实定义了erase
,但没有定义value_type
,那么您可以通过从迭代器中获取value_type:来回避直接定义value_type
的要求
template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f) {
using It = decltype(c.begin());
c.erase(std::remove_if(c.begin(), c.end(),
[&f](const std::iterator_traits<It>::value_type& x) {
return !f(x);
}),
c.end());
}
使用iterator_traits<T>::value_type
,当迭代器实际上是指针时,您可以(例如(获得指针对象类型。不过,当您已经需要begin()
、end()
和(尤其是(erase
时,我不知道在这种情况下有什么实际优势。我们可以通过使用std::begin(c)
和std::end(c)
来消除对begin()
和end()
作为成员的要求,但(再次(当我们仍然需要erase
成员时,这并不能真正为我们带来任何有意义的东西(比如使用数组的能力(。
一个更简单的方法是使用std::partition
:
template <typename Container, typename Filter>
void filter_ip(Container& c, Filter&& f) {
c.erase(std::partition(c.begin(), c.end(), f), c.end());
}
这确实有一个缺点,即它可以(将(重新排列它保留的元素,所以如果你真的需要保留原始顺序,它就不起作用了。如果复制/移动构造比交换便宜得多,这也可能会降低效率(但这相当罕见(。
最后一种可能性是自己实现算法,而不是委托给另一种算法:
template <typename Container, typename Filter>
void filter2(Container& c, Filter&& f) {
auto dst = c.begin();
for (auto src = dst; src != c.end(); ++src)
if (f(*src)) {
*dst = *src;
++dst;
}
c.erase(dst, c.end());
}
如果你喜欢避免自我分配,你可以添加:
while (f(*dst))
++dst;
在上述CCD_ 31循环之前。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 可组合的lambda/std::函数与std::可选
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 如何建立使用模板函数的lambda函数的尾部返回类型
- If语句未被求值C++
- 如何将lambda作为模板类的成员函数参数
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 在 lambda 捕获中声明的变量的类型推导
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 如何知道QDataStream不能反序列化某些内容
- 我可以将调用类的"this"传递给 lambda 函数吗?
- 为什么lambda在clang上崩溃而不是在gcc上崩溃
- 模板函数指针和lambda
- 如何按位获取数字而不是求反符号位
- 使用运算符!对重载的布尔谓词求反
- 用cuda中的高斯-约旦方法求复数的矩阵反演
- GCC constexpr lambda in constexpr functions 和编译时求值
- Lambda-expression作为常量表达式的未求值子表达式
- 将求反的double强制转换为int
- 在不知道参数类型的情况下对lambda求反