跟我学C++中级篇——std::not_fn

一、std::not_fn定义和说明

std::not_fn这个模板函数非常有意思,在前面我们学习过wrapper(包装器),其实它就是通过封装一个包装器来实现返回值的非。它的基本定义如下:

template< class F >
/* 未指定 */ not_fn( F&& f );(1)	(C++17)(C++20 起为 constexpr)
template< auto ConstFn >
constexpr /* 未指定 */ not_fn() noexcept;(2)	(C++26)

这里面的f参数是一个Callable对象。它的受限条件为:
1、std::decay_t 须为可调用 (Callable) 并支持移动构造 (MoveConstructible)
2、std::is_constructible_v<std::decay_t, F> 的结果必须为 true
那么,问题就来了,为什么要搞这么简单的一个东西呢?直接操作不更简单么?

二、应用

老生再次常谈一下,一切的技术的应用,跟场景的结合是无法独立出来的。也就是说,std::not_fn的应用,也不是放置四海皆优秀的。在实际的开发中,可能会遇到很多种情况,比如普通的函数,类成员函数,仿函数,甚至新标准中的Lambda表达式等等。
在处理这些情况的时候儿,可能直接操作返回一个非的结果很简单,也可能比较不简单。更有可能虽然简单但不好理解。而有的情况下开发者需要的不是一个简单的结果而是一个非的函数,凡此种种,都可能会有不同的需要。
一般来说这种在上层进行封装的应用,大多数情况下在底层应用比较多,比如本身就是库或框架。一如前面看到的STL中的元函数(如std::is_integral等),在业务层展现应用的机会很少,但一旦到底层的库编程,则应用大行其道。

三、混合应用

开发者经常会遇到这种问题,实现一个问题的一面(正面或反面)比较简单,而实现另外一面则相对复杂一些。这种简单和复杂不单指的实现上的简单,也包括代码阅读上的简单。比如实现一个返回True,需要一行代码,而返回False则需要几行代码。这种情况下,直接修改函数本身是没有问题的,但应用起来就等于是多实现了一次,而且如果需要将函数本身做为参数传递的话,这又是一个问题,很有可能在嵌套传参时导致行为的变形。
这时,使用std::not_fn直接生成一个新的非的函数,应用起来就会非常清晰明了。下面看一个简单的例程:

#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>// 是否为偶数
bool is_even(int x) {return x % 2 == 0;
}int main() {std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};auto is_odd = std::not_fn(is_even);// 打印std::vector<int> odd_numbers;std::copy_if(numbers.begin(), numbers.end(), std::back_inserter(odd_numbers), is_odd);std::cout << "Odd numbers: ";for (int num : odd_numbers) {std::cout << num << " ";}std::cout << std::endl;return 0;
}

其实在基础库的很类似的应用 中都可以应用到std::not_fn,在代码层级更容易理解。

四、特点

std::not_fn主要是为了替代前面的替代std::not1,std::not2这类非相关的处理。毕竟后两个依赖于std::unary_function 或 std::binary_function的实现。从设计角度看,这种应用方式首先就限定的灵活性及通用性或者理解为适配性不好。那么std::not_fn有什么特点呢:
1、优势
a)更高一层的抽象可以让开发者离开代码内部的实现,专注于逻辑的实现
b)适应性强,对所有的类函数(如普通函数、类内部成员函数、仿函数等等)都支持
c)c++20后支持constexpr,可在编译期进行优化
d)代码更直观,容易理解和维护
2、劣势
a)过深的嵌套可能逻辑的复杂化和提高后期维护的复杂度
b)应用范围受限,一般要求返回值的类型必须为可转化为bool类型的数据类型
c)在很简单的非应用时,std::not_fn并没有优势

所以开发者的头脑中始终要有根弦警惕着,技术要善用而非乱用!

五、例程

下面看一个cppreference的例程:

#include <cassert>
#include <functional>bool is_same(int a, int b) noexcept
{return a == b;
}struct S
{int val;bool is_same(int arg) const noexcept { return val == arg; }
};int main()
{// 用于自由函数:auto is_differ = std::not_fn(is_same);assert(is_differ(8, 8) == false); // 等价于:!is_same(8, 8) == falseassert(is_differ(6, 9) == true); // 等价于:!is_same(8, 0) == true// 用于成员函数:auto member_differ = std::not_fn(&S::is_same);assert(member_differ(S{3}, 3) == false); // 等价于:S tmp{6}; !tmp.is_same(6) == false// 保持 noexcept 说明:static_assert(noexcept(is_differ) == noexcept(is_same));static_assert(noexcept(member_differ) == noexcept(&S::is_same));// 用于函数对象:auto same = [](int a, int b) { return a == b; };auto differ = std::not_fn(same);assert(differ(1, 2) == true); // 等价于:!same(1, 2) == trueassert(differ(2, 2) == false); // 等价于:!same(2, 2) == false#if __cpp_lib_not_fn >= 202306Lauto is_differ_cpp26 = std::not_fn<is_same>();assert(is_differ_cpp26(8, 8) == false);assert(is_differ_cpp26(6, 9) == true);auto member_differ_cpp26 = std::not_fn<&S::is_same>();assert(member_differ_cpp26(S{3}, 3) == false);auto differ_cpp26 = std::not_fn<same>();static_assert(differ_cpp26(1, 2) == true);static_assert(differ_cpp26(2, 2) == false);
#endif
}

例程非常简单,其实可以理解为对函数指针F的一种非的反向控制。它可以提供更灵活的控制方式,而不必直接修改相关的代码。

六、总结

std::not_fn本身并没有什么难度。但只要认真想一下,其实就难明白,它其实就是让编程变得更灵活和更容易控制。尽最大可能的减少对程序的整体的影响或产生副作用。特别是对已经存在的老的代码的完善和更新的情况下,这种处理方式是一种非常合理和便捷的存在。
当把一个操作看成一个黑盒时,它们内外的交互就不存在了,那么无论对哪一方来说,这都是好事儿。简单永远是开发者追求的目标!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/73053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阶跃星辰开源300亿参数视频模型Step-Video-TI2V:运动可控+102帧长视频生成

阶跃星辰&#xff08;StepFun&#xff09;正式开源其新一代图生视频模型 Step-Video-TI2V &#xff0c;该模型基于300亿参数的Step-Video-T2V训练&#xff0c;支持文本与图像联合驱动生成长达102帧的高质量视频&#xff0c;在运动控制与场景适配性上实现突破。 核心亮点 …

java查询es超过10000条数据

java查询es超过10000条数据 背景:需要每天零点导出es中日志数据到数据库中给数据分析人员做清洗&#xff0c;然后展示给业务人员。但在es中默认一次最多只能查询10000条数据。 在这里我就只贴一下关键代码 SearchRequest searchRequest new SearchRequest("索引名"…

使用 libevent 构建高性能网络应用

使用 libevent 构建高性能网络应用 在现代网络编程中&#xff0c;高性能和可扩展性是开发者追求的核心目标。为了实现这一目标&#xff0c;许多开发者选择使用事件驱动库来管理 I/O 操作和事件处理。libevent 是一个轻量级、高性能的事件通知库&#xff0c;广泛应用于网络服务…

HeyGem.ai 全离线数字人生成引擎加入 GitCode:开启本地化 AIGC 创作新时代

在人工智能技术飞速演进的时代&#xff0c;数据隐私与创作自由正成为全球开发者关注的焦点。硅基智能旗下开源项目 HeyGem.ai 近日正式加入 GitCode&#xff0c;以全球首个全离线数字人生成引擎的颠覆性技术&#xff0c;重新定义人工智能生成内容&#xff08;AIGC&#xff09;的…

【leetcode hot 100 39】组合总和

错误解法一&#xff1a;每一次回溯都遍历提供的数组 class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> result new ArrayList<List<Integer>>();List<Integer> te…

VSCODE右下角切换环境没用

VSCODE惦记右下角python版本&#xff0c;切换别的虚拟环境时&#xff0c;始终切换不了&#xff0c;同时右下角弹出&#xff1a; Client Pylance: connection to server is erroring. 取消继承环境也改了。https://www.cnblogs.com/coreylin/p/17509610.html 还是不行&#xf…

【sql靶场】第23、25,25a关过滤绕过保姆级教程

目录 【sql靶场】第23、25-28关过滤绕过保姆级教程 第二十三关 第二十五关 1.爆出数据库 2.爆出表名 3.爆出字段 4.爆出账号密码 【sql靶场】第23、25&#xff0c;25a关过滤绕过保姆级教程 第二十三关 从本关开始又是get传参&#xff0c;并且还有了对某些字符或字段的过…

python每日十题(5)

保留字&#xff0c;也称关键字&#xff0c;是指被编程语言内部定义并保留使用的标识符。Python 3.x版本中有35个保留字&#xff0c;分别为&#xff1a;and, as,assert,async,await,break,class,continue,def,del,elif,else, except, False, finally,for,from,global, if,import…

Pytorch使用手册—自定义 C++ 和 CUDA 扩展(专题五十二)

提示 从 PyTorch 2.4 开始,本教程已被废弃。请参考 PyTorch 自定义操作符,了解关于通过自定义 C++/CUDA 扩展扩展 PyTorch 的最新指南。 PyTorch 提供了大量与神经网络、任意张量代数、数据处理等相关的操作。然而,您可能仍然会发现自己需要一个更自定义的操作。例如,您可能…

CHM(ConcurrentHashMap)中的 sizeCtl 的作用与值变化详解

学海无涯&#xff0c;志当存远。燃心砺志&#xff0c;奋进不辍。愿诸君得此鸡汤&#xff0c;如沐春风&#xff0c;学业有成。若觉此言甚善&#xff0c;烦请赐赞一枚&#xff0c;共励学途&#xff0c;同铸辉煌 ConcurrentHashMap常简写为CHM&#xff0c;尤其是在讨论并发编程时。…

VLAN综合实验报告

一、实验拓扑 网络拓扑结构包括三台交换机&#xff08;LSW1、LSW2、LSW3&#xff09;、一台路由器&#xff08;AR1&#xff09;以及六台PC&#xff08;PC1-PC6&#xff09;。交换机之间通过Trunk链路相连&#xff0c;交换机与PC、路由器通过Access或Hybrid链路连接。 二、实验…

OpenGL ES ->计算多个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap

XML文件 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"><…

Java线程池深度解析:从使用到调优

适合人群&#xff1a;Java中级开发者 | 并发编程入门者 | 系统调优实践者 目录 一、引言&#xff1a;为什么线程池是Java并发的核心&#xff1f; 二、线程池核心知识点详解 1. 线程池核心参数与原理 2. 线程池的创建与使用 (1) 基础用法示例 (2) 内置线程池的隐患 3. 线…

【工具变量】全国地级市地方ZF债务数据集(2014-2023年)

地方ZF债务是地方财政运作的重要组成部分&#xff0c;主要用于基础设施建设、公共服务及经济发展&#xff0c;是衡量地方财政健康状况的重要指标。近年来&#xff0c;我国地级市的地方ZF债务规模不断变化&#xff0c;涉及一般债务和专项债务等多个方面&#xff0c;对金融市场、…

大模型训练的调参与算力调度技术分析

大模型训练的调参与算力调度 虽然从网络上&#xff0c;还有通过和大模型交流&#xff0c;了解了很多训练和微调的技术。但没有实践&#xff0c;也没有什么机会实践。因为大模型训练门槛还是挺高的&#xff0c;想要有一手资料比较困难。如果需要多机多卡&#xff0c;硬件成本小…

深入理解 lt; 和 gt;:HTML 实体转义的核心指南!!!

&#x1f6e1;️ 深入理解 < 和 >&#xff1a;HTML 实体转义的核心指南 &#x1f6e1;️ 在编程和文档编写中&#xff0c;< 和 > 符号无处不在&#xff0c;但它们也是引发语法错误、安全漏洞和渲染混乱的头号元凶&#xff01;&#x1f525; 本文将聚焦 <&#…

GRS认证的注意事项!GRS认证的定义

GRS认证的注意事项&#xff0c;对于企业而言&#xff0c;是通往可持续发展和环保生产道路上的重要里程碑。在追求这一认证的过程中&#xff0c;企业必须细致入微&#xff0c;确保每一个环节都符合严格的标准与要求。 首先&#xff0c;企业必须全面理解GRS认证的核心原则&#…

位运算--求二进制中1的个数

位运算–求二进制中1的个数 给定一个长度为 n 的数列&#xff0c;请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行包含整数 n。 第二行包含 n 个整数&#xff0c;表示整个数列。 输出格式 共一行&#xff0c;包含 n 个整数&#xff0c;其中的第 i 个数表…

Linux常用指令(3)

大家好,今天我们继续来介绍一下linux常用指令的语法,加深对linux操作系统的了解,话不多说,来看. 1.rmdir指令 功能&#xff1a;删除空目录 基本语法&#xff1a; rmdir 要删除的空目录 ⭐️rmdir删除的是空目录,如果目录下有内容是无法删除 2.mkdir指令 功能&#xff1a;创…

《Linux 网络架构:基于 TCP 协议的多人聊天系统搭建详解》

一、系统概述 本系统是一个基于 TCP 协议的多人聊天系统&#xff0c;由一个服务器和多个客户端组成。客户端可以连接到服务器&#xff0c;向服务器发送消息&#xff0c;服务器接收到消息后将其转发给其他客户端&#xff0c;实现多人之间的实时聊天。系统使用 C 语言编写&#x…