std::not_fn是c++17引入的函数对象包装器,对原谓词结果执行逻辑非而非按位取反;它要求被包装对象签名明确、注意捕获变量生命周期,并且operator()为const,不支持可变谓词。

std::not_fn 本质是包装器,不是简单取反布尔值
它不直接对谓词返回值做 ! 运算,而是构造一个新函数对象,调用时自动将原谓词结果取反。这意味着:如果原谓词返回 int、long 或其他可隐式转为 bool 的类型,std::not_fn 仍按逻辑非处理(即 !static_cast<bool>(result)</bool>),而非按位取反或数值取负。
常见误用是以为它能“反转比较方向”,比如想把 std::less<int></int> 变成 std::greater<int></int> —— 实际上 std::not_fn<:less>>{}(3, 5)</:less> 返回 true(因为 3 是 <code>true,取反得 false?等等,不对:注意 std::not_fn 是对调用结果取反,所以 std::not_fn<:less>>{}(3, 5)</:less> 等价于 !(3 → <code>false;而 std::not_fn<:less>>{}(5, 3)</:less> 是 !(5 → <code>true。它并不等价于 std::greater,只是语义上“不满足小于关系”,包含等于和大于两种情况。
必须传入可调用对象,不能直接传 bool 或字面量
std::not_fn 接收的是一个**可调用实体**(函数指针、lambda、函数对象),不是布尔表达式本身。下面写法全部错误:
-
std::not_fn(3 > 5)——3 > 5是false(bool值),不可调用 -
std::not_fn([]{ return true; })—— lambda 无参数,但后续调用时若传参会编译失败 -
std::not_fn(std::isalnum)——std::isalnum有重载,未显式指定类型,编译器无法推导
正确做法是确保被包装对象签名明确、可调用:
立即学习“C++免费学习笔记(深入)”;
auto is_not_digit = std::not_fn(static_cast<int(*)(int)>(std::isdigit));
auto not_less = std::not_fn(std::less<int>{});
auto not_even = std::not_fn([](int x) { return x % 2 == 0; });
捕获 lambda 和引用绑定需格外小心生命周期
如果被 std::not_fn 包装的 lambda 捕获了局部变量,或通过 std::ref 绑定非常量左值,那么 std::not_fn 对象的生命周期必须不长于其所依赖对象的生命周期。否则调用时触发未定义行为。
- 错误示例:
auto make_checker = [&x]() { return std::not_fn([&]{ return x > 0; }); }—— 若x是栈变量且make_checker返回的函数对象在x销毁后被调用,就崩了 - 安全做法:用值捕获
[x],或确保引用对象长期有效(如静态变量、类成员) -
std::not_fn(std::ref(pred))同样受pred生命周期约束,别以为加了std::ref就万事大吉
替代方案:C++20 起可用 ranges::not_fn,更泛化但需适配范围算法
std::not_fn(C++17)仅用于普通函数对象组合;C++20 引入的 std::ranges::not_fn 是为 std::ranges 算法设计的,支持 ADL 友好、完美转发与投影(projection)感知。例如:
std::vector<int> v = {1, 2, 3, 4, 5};
auto it = std::ranges::find_if(v, std::ranges::not_fn(std::odd));
// 注意:std::odd 是 C++20 新的谓词,实际应写 std::ranges::odd 或自定义
但注意:两者不可混用 —— std::not_fn 包装的函数对象不能直接喂给 std::ranges::xxx 算法作谓词(除非你手动适配),反之亦然。项目若已用 C++20 ranges,优先考虑 std::ranges::not_fn;否则老老实实用 std::not_fn,别强行升级。
最易被忽略的一点:std::not_fn 包装后的对象,其 operator() 是 const 成员函数,因此无法用于需要修改内部状态的可变谓词(比如带 mutable lambda 且依赖捕获变量自增的场景)—— 它强制以 const 方式调用原谓词。










