You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于已有谓词定义C++通用not高阶函数?

实现通用的逻辑否定高阶函数及扩展方案

首先得说,你的需求非常合理——谁都不想每次过滤都写重复的否定lambda,而且能扩展到其他逻辑操作就更方便了。咱们一步步解决这个问题:

先解决核心问题:写一个通用的not_fn高阶函数

你之前的实现踩了几个坑:用std::function带来类型擦除开销、引用捕获导致悬空、还误用了C++关键字not(这是保留字,不能当函数名)。这里给你一个更通用、高效的版本:

#include <utility> // 用于std::forward和std::move

template <typename Predicate>
auto not_fn(Predicate pred) {
    // 移动捕获谓词,避免不必要的拷贝(尤其是大的函数对象)
    return [pred = std::move(pred)](auto&&... args) {
        // 完美转发参数,适配任意签名的谓词
        return !pred(std::forward<decltype(args)>(args)...);
    };
}

这个版本的优势:

  • 不需要std::function,没有类型擦除的性能损耗
  • 支持任意签名的谓词(不止bool(const MyType*),比如带多个参数、成员函数指针都可以)
  • 完美转发参数,保证谓词接收到的参数类型和值类别正确
  • 移动捕获避免拷贝开销,更高效

你的使用场景可以改成这样(注意:remove_if只是把元素移到容器末尾,必须配合erase才会真正删除元素):

#include <algorithm>
#include <vector>

using namespace std;
vector<const MyType*> elems = ...;
// 删除所有匹配my_pred的元素,剩下的就是不匹配的
elems.erase(remove_if(begin(elems), end(elems), not_fn(my_pred)), end(elems));

为什么你的原实现行不通?

咱们拆解下你写的代码问题:

template<typename T> function<bool(T)> not(function<bool(T)> pred) { return [&](T x) { return !pred(x) }; }
  1. 关键字冲突:not是C++的保留关键字,不能用作函数名,这是编译报错的直接原因之一
  2. 悬空引用:[&]捕获的是函数参数pred的引用,函数执行完毕后pred会被销毁,返回的lambda里的引用就变成悬空的,调用时会触发未定义行为
  3. 通用性差:只能接受std::function<bool(T)类型的谓词,像普通函数指针、lambda这些无法隐式转换的类型就用不了,而且只能处理单参数的谓词

关于std::logical_not的正确打开方式

你之前用std::logical_not失败,是因为它默认是用来否定值,而不是包装谓词。如果一定要用它,可以结合std::bind,但写法很繁琐:

#include <functional>

using namespace std::placeholders;
elems.erase(remove_if(begin(elems), end(elems), 
    std::bind(std::logical_not<bool>(), std::bind(my_pred, _1))), 
end(elems));

显然,这种写法远不如咱们自己写的not_fn简洁直观。

扩展到其他逻辑操作符

既然你想扩展到与、或等操作,咱们可以用同样的模板思路实现:

逻辑与(同时满足两个谓词)

template <typename Pred1, typename Pred2>
auto and_fn(Pred1 pred1, Pred2 pred2) {
    return [pred1 = std::move(pred1), pred2 = std::move(pred2)](auto&&... args) {
        return pred1(std::forward<decltype(args)>(args)...) 
            && pred2(std::forward<decltype(args)>(args)...);
    };
}

逻辑或(满足任意一个谓词)

template <typename Pred1, typename Pred2>
auto or_fn(Pred1 pred1, Pred2 pred2) {
    return [pred1 = std::move(pred1), pred2 = std::move(pred2)](auto&&... args) {
        return pred1(std::forward<decltype(args)>(args)...) 
            || pred2(std::forward<decltype(args)>(args)...);
    };
}

用法示例:比如保留既不匹配my_pred也不匹配another_pred的元素:

// 删除匹配任意一个谓词的元素
elems.erase(remove_if(begin(elems), end(elems), or_fn(my_pred, another_pred)), end(elems));

C++20及以上的更优雅方案

如果你用的是C++20或更高版本,标准库已经帮你做好了这些:

  1. 标准库自带std::not_fn,直接用就行,不用自己实现
  2. 用std::erase_if可以一步完成“删除匹配谓词的元素”,不用写erase+remove_if:
#include <vector>

std::erase_if(elems, my_pred); // 直接删除所有匹配my_pred的元素,剩下的就是你要的
  1. 还可以用范围库的std::views::filter直接生成过滤后的视图:
#include <ranges>

// 生成过滤后的视图(不拷贝元素,只是视图)
auto filtered_view = elems | std::views::filter(std::not_fn(my_pred));
// 如果需要转换成vector:
std::vector<const MyType*> filtered_elems(filtered_view.begin(), filtered_view.end());

内容的提问来源于stack exchange,提问作者Gaston Simone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:34