C++ 在不知道参数类型的情况下求反lambda?
我正在尝试编写一个就地过滤器函数,其工作原理与Python的过滤器类似。例如: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
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;