跟我學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.pswp.cn/pingmian/73053.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73053.shtml
英文地址,請注明出處:http://en.pswp.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…