C++ 在不知道参数类型的情况下求反lambda?

C++ 在不知道参数类型的情况下求反lambda?,c++,templates,c++11,types,lambda,C++,Templates,C++11,Types,Lambda,我正在尝试编写一个就地过滤器函数,其工作原理与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} 然而,它似乎不太理想,因为以前它只要求容器具有开始、结束、和擦除,而现在它还要求定义一个值类型。而且它看起来有点笨重 这是第二种方法。第一个将使用std::not1(st

我正在尝试编写一个就地过滤器函数,其工作原理与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}
然而,它似乎不太理想,因为以前它只要求
容器
具有
开始
结束
、和
擦除
,而现在它还要求定义一个
值类型
。而且它看起来有点笨重

这是第二种方法。第一个将使用
std::not1(std::function(f))
而不是lambda,后者仍然需要该类型

我还尝试将arg func指定为具有已知参数类型的
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());
}
模板
无效过滤器ip(容器和c、标准::函数和f)
{
c、 擦除(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&)>'
“main():”不是从“std::function”派生的

这有什么办法吗?直观地看,它应该非常简单,因为您所需要做的就是将not应用于您已经知道的
f
返回的bool。

如果您不能使用C++14泛型lambdas,那么使用模板化的
运算符()将其委托给一个经典的函子怎么样

然后,因为它会摇晃:

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) );
}
模板
如果(C&&C,F&&F)则无效删除{
使用std::begin;使用std::end;
c、 擦除(标准::删除_if(开始(c),结束(c),标准::前进(f)),结束(c));
}
我们得到:

std::vector<int> x = {1, 2, 3, 4, 5};
erase_remove_if(x, not_f([](int i){return i>=3;}));
std::vector x={1,2,3,4,5};
如果(x,而不是f([](int i){return i>=3;})),则删除;

在我看来,如果您已经需要
开始
结束
擦除
,那么还需要
值类型
是一个很小的增加。如果您不需要使用
擦除
,那么您至少可以获得一些真正的容器,但是取消对
值类型的要求并没有多大效果

尽管如此,如果您的容器确实定义了
erase
,但没有定义
value\u-type
,您可以通过从迭代器中获取value\u-type来绕过它定义
value\u-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());
}
这样做的缺点是它可以(将)重新排列它保留的元素,因此如果您确实需要保留原始顺序,它将无法工作。如果复制/移动构造比交换便宜得多,那么这种方法的效率也会降低(但这种情况相当少见)

最后一种可能性是,只需自己实现算法,而不是委托给另一个算法:

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());
}

…在上面的
for
循环之前。

C++14具有通用lambda。它们会很好地适应。最好推断
operator()
的返回类型,以允许通过SFINAE检测可调用的。此外,
negate
可能不是最好的名称,因为
std::negate
使用一元
-
(减号),而不是
(逻辑非)。——哦,通常会添加一个工厂函数模板用于类型推断。多态lambda缺少
而不是
的SFINAE友好性。(你当然知道,但不是每个人都知道。)@dyp nod:1行添加和1行修改,以添加sfinae对14的支持。如果我的眼睛没有欺骗我,你就错过了
中,而不是在
中。另外,包含
可能更合适一些用于确定返回类型。我想到的一个例子是返回指针(比如迭代器)的函数,它可以通过
进行调整
成为谓词。OTOH,将
not_f
限制为仅对谓词进行操作可能更合理。同意@dyp,但这肯定会让sfinae难看。使我希望使用
struct而不是
使其更简单。而且可能是一个智能的、递归的
result\u of
。事实上,它现在甚至比重复的
decltype(!f(…)
还要长。如果它不短于返回表达式的重复,有什么理由更喜欢
result\u of
吗?非常明智,先生。在计算它是否适用于数组时,我进行了类似的推理,即数组没有
擦除
方法。我可能只会依赖
value\u-type
,但如果我不依赖,我最喜欢
std::iterator\u traits::value\u-type
方法。
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)};
}
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;}));
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());
}
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;