C++模板梳理

目錄

函數模板

類模板

變量模板

模板全特化

模板偏特化

模板顯式實例化解決文件分離問題

折疊表達式

模板的二階段編譯

待決名(dependent name)

SFINAE

概念與約束


函數模板

函數模板不是函數,只有實例化的函數模板,編譯器才能生成實際的函數定義,不過在很多時候,它看起來就像普通函數一樣。

示例:

下面是一個函數模板,返回兩個對象中較大的那個:

template<typename T>
T max(T a,T b){return a > b ? a : b;
}

這里其實對T有幾個要求,1:有>運算符,比如內置的int,double; 2:返回了一個T,即要求T是可以移動或復制的。 如果函數模板實參不滿足以上要求,則匹配不到此模板。

C++17 之前,類型 T 必須是可復制或移動才能傳遞參數。C++17 以后,即使復制構造函數和移動構造函數都無效,因為 C++17 強制的復制消除的存在,也可以傳遞臨時純右值。

使用模板

template<typename T>
T max(T a, T b) {return a > b ? a : b;
}int main(){int a{ 1 };int b{ 2 };max(a, b);          // 函數模板 max 被推導為 max<int>max<double>(a, b);  // 傳遞模板類型實參,函數模板 max 為 max<double>
}

模板函數可手動指定模板形參類型,也可以讓編譯器推導,即模板參數推導(template argument deduction),c++11支持函數模板參數推導,但是類模板參數推導要到c++17才支持。

對于編譯無法推導的場景,可手動指定,如:

max<double>(1, 1.2);           
max<std::string>("luse"s, "樂");

但是 std::string 沒有辦法如此操作,編譯器會報:

<source>:31:4: error: call of overloaded 'max(std::string, std::string)' is ambiguous

31 | max(string("luse"), string("樂"));

| ~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

<source>:23:3: note: candidate: 'T max(const T&, const T&) [with T = std::__cxx11::basic_string<char>]'

23 | T max(const T& a, const T& b) {

| ^~~

/opt/compiler-explorer/gcc-13.2.0/include/c++/13.2.0/bits/stl_algobase.h:257:5: note: candidate: 'constexpr const _Tp& std::max(const _Tp&, const _Tp&) [with _Tp = __cxx11::basic_string<char>]'

257 | max(const _Tp& __a, const _Tp& __b)

即函數有二義性,這是因為自己所編寫的max函數和標準庫的max函數沖突了,但是int double實例化max函數并不會。原因是string在std命名空間,標準庫的max函數也在std命名空間,雖然我們沒有使用?std::,但是根據 C++ 的查找規則,(實參依賴查找)ADL,依然可以查找到。

那么我們如何解決呢?很簡單,進行有限定名字查找,即使用?::?或?std::?說明,你到底要調用 “全局作用域”的 max,還是 std 命名空間中的 max。

::max(string("luse"), std::string("樂"));

有默認實參的模板類型形參

就如同函數形參可以有默認值一樣,模板形參也可以有默認值。

template<typename T = int>
void f();f();            // 默認為 f<int>
f<double>();    // 顯式指明為 f<double>using namespace std::string_literals;template<typename T1,typename T2,typename RT = decltype(true ? T1{} : T2{}) >RT max(const T1& a, const T2& b) { // RT 是 std::stringreturn a > b ? a : b;
}int main(){auto ret = ::max("1", "2"s);std::cout << ret << '\n';
}

讓 max 函數模板接受兩個參數的時候不需要再是相同類型,那么這自然而然就會引入另一個問題了,如何確定返回類型?

typename RT = decltype(true ? T1{} : T2{})

這是一個三目運算符表達式。然后外面使用了 decltype 獲取這個表達式的類型,那么問題是,為什么是 true 呢?以及為什么需要 T1{},T2{} 這種形式?

1:我們為什么要設置為?true

其實無所謂,設置 false 也行,true 還是 false 不會影響三目表達式的類型。這涉及到了一些復雜的規則,簡單的說就是三目表達式要求第二項和第三項之間能夠隱式轉換,然后整個表達式的類型會是?“公共”類型

比如第二項是 int 第三項是 double,三目表達式當然會是 double。

using T = decltype(true ? 1 : 1.2);
using T2 = decltype(false ? 1 : 1.2);

2:為什么需要?T1{}T2{}?這種形式?

沒有辦法,必須構造臨時對象來寫成這種形式,這里其實是不求值語境,我們只是為了寫出這樣一種形式,讓 decltype 獲取表達式的類型罷了。

模板的默認實參的和函數的默認實參大部分規則相同。

decltype(true ? T1{} : T2{})?解決了。

事實上上面的寫法都十分的丑陋與麻煩,我們可以使用 auto 簡化這一切。

template<typename T,typename T2>
auto max(const T& a, const T2& b) -> decltype(true ? a : b){return a > b ? a : b;
}

這是 C++11 后置返回類型,它和我們之前用默認模板實參?RT?的區別只是稍微好看了一點嗎?

不,它們的返回類型是不一樣的,如果函數模板的形參是類型相同?true ? a : b?表達式的類型是?const T&;如果是?max(1, 2)?調用,那么也就是?const int&而前面的例子只是?T?即?int(前面都是用模板類型參數直接構造臨時對象,而不是有實際對象,自然如此,比如?T{})。

使用 C++20 簡寫函數模板,我們可以直接再簡化為:

decltype(auto) max(const auto& a, const auto& b)  {return a > b ? a : b;
}

效果和上面使用后置返回類型的寫法完全一樣;C++14 引入了兩個特性:

  1. 返回類型推導(也就是函數可以直接寫 auto 或 decltype(auto) 做返回類型,而不是像 C++11 那樣,只是后置返回類型。

  2. decltype(auto)?“如果返回類型沒有使用 decltype(auto),那么推導遵循模板實參推導的規則進行”。我們上面的?max?示例如果不使用 decltype(auto),按照模板實參的推導規則,是不會有引用和 cv 限定的,就只能推導出返回?T?類型。即直接寫auto會丟棄CV限定符,但decltype(auto)按decltype的規則推導類型,會保留CV限定符。

非類型模板形參

既然有”類型模板形參“,自然有非類型的,顧名思義,也就是模板不接受類型,而是接受值或對象。

template<std::size_t N>
void f() { std::cout << N << '\n'; }f<100>();

非類型模板形參有眾多的規則和要求,目前,你簡單認為需要參數是“常量”即可。

非類型模板形參當然也可以有默認值:

template<std::size_t N = 100>
void f() { std::cout << N << '\n'; }f();     // 默認      f<100>
f<66>(); // 顯式指明  f<66>

重載函數模板?

函數模板與非模板函數可以重載。

這里會涉及到非常復雜的函數重載決議,即選擇到底調用哪個函數。

我們用一個簡單的示例展示一部分即可:

template<typename T>
void test(T) { std::puts("template"); }void test(int) { std::puts("int"); }test(1);        // 匹配到test(int)
test(1.2);      // 匹配到模板
test("1");      // 匹配到模板
  • 通常優先選擇非模板的函數。

可變參數模板

和其他語言一樣,C++ 也是支持可變參數的,我們必須使用模板才能做到。

老式 C 語言的變長實參有眾多弊端,參見。

同樣的,它的規則同樣眾多繁瑣,我們不會說太多,以后會用到的,我們當前還是在入門階段。

我們提一個簡單的需求:

我需要一個函數 sum,支持 sum(1,2,3.5,x,n...) 即函數 sum 支持任意類型,任意個數的參數進行調用,你應該如何實現?

首先就要引入一個東西:形參包

本節以?C++14?標準進行講述。

模板形參包是接受零個或更多個模板實參(非類型、類型或模板)的模板形參。函數形參包是接受零個或更多個函數實參的函數形參。

template<typename...Args>
void sum(Args...args){}

這樣一個函數,就可以接受任意類型的任意個數的參數調用,我們先觀察一下它的語法和普通函數有什么不同。

模板中需要 typename 后跟三個點 Args,函數形參中需要用模板類型形參包后跟著三個點 再 args。

args 是函數形參包,Args 是類型形參包,它們的名字我們可以自定義。

args 里,就存儲了我們傳入的全部的參數,Args 中存儲了我們傳入的全部參數的類型。

那么問題來了,存儲很簡單,我們要如何把這些東西取出來使用呢?這就涉及到另一個知識:形參包展開。

void f(const char*, int, double) { puts("值"); }
void f(const char**, int*, double*) { puts("&"); }template<typename...Args>
void sum(Args...args){  // const char * args0, int args1, double args2f(args...);   // 相當于 f(args0, args1, args2)f(&args...);  // 相當于 f(&args0, &args1, &args2)
}int main() {sum("luse", 1, 1.2);
}

?

類模板

變量模板

模板全特化

模板偏特化

模板顯式實例化解決文件分離問題

折疊表達式

模板的二階段編譯

待決名(dependent name)

SFINAE

概念與約束

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79268.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79268.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79268.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據鏈共享:從印巴空戰到工業控制的跨越性應用

摘要 本文通過對印巴空戰中數據鏈共享發揮關鍵作用的分析&#xff0c;引出數據鏈共享在工業控制領域同樣具有重大價值的觀點。深入闡述 DIOS 工業控制操作系統作為工業數據鏈共享基礎技術的特點、架構及應用優勢&#xff0c;對比空戰場景與工業控制場景下數據鏈共享的相…

巡檢機器人數據處理技術的創新與實踐

摘要 隨著科技的飛速發展&#xff0c;巡檢機器人在各行業中逐漸取代人工巡檢&#xff0c;展現出高效、精準、安全等顯著優勢。當前&#xff0c;巡檢機器人已從單純的數據采集階段邁向對采集數據進行深度分析的新階段。本文探討了巡檢機器人替代人工巡檢的現狀及優勢&#xff0c…

在 Flink + Kafka 實時數倉中,如何確保端到端的 Exactly-Once

在 Flink Kafka 構建實時數倉時&#xff0c;確保端到端的 Exactly-Once&#xff08;精確一次&#xff09; 需要從 數據消費&#xff08;Source&#xff09;、處理&#xff08;Processing&#xff09;、寫入&#xff08;Sink&#xff09; 三個階段協同設計&#xff0c;結合 Fli…

當可視化遇上 CesiumJS:突破傳統,打造前沿生產配套方案

CesiumJS 技術基礎介紹 CesiumJS 是一款基于 JavaScript 的開源庫&#xff0c;專門用于創建動態、交互式的地理空間可視化。它利用 WebGL 技術&#xff0c;能夠在網頁瀏覽器中流暢地渲染高分辨率的三維地球和地圖場景。CesiumJS 支持多種地理空間數據格式&#xff0c;包括但不…

RabbitMQ深入學習

繼續上一節的學習&#xff0c;上一節學習了RabbitMQ的基本內容&#xff0c;本節學習RabbitMQ的高級特性。 RocketMQ的高級特性學習見這篇博客 目錄 1.消息可靠性1.1生產者消息確認1.2消息持久化1.3消費者消息確認1.4消費失敗重試機制1.5消息可靠性保證總結 2.什么是死信交換機…

Linux系統:虛擬文件系統與文件緩沖區(語言級內核級)

本節重點 初步理解一切皆文件理解文件緩沖區的分類用戶級文件緩沖區與內核級文件緩沖區用戶級文件緩沖區的刷新機制兩級緩沖區的分層協作 一、虛擬文件系統 1.1 理解“一切皆文件” 我們都知道操作系統訪問不同的外部設備&#xff08;顯示器、磁盤、鍵盤、鼠標、網卡&#…

在c++中老是碰到string,這是什么意思?

定義一個string類型變量的引用&#xff0c;相當于給現有變量起個別名&#xff0c;與指針還是不一樣的。比如string a;string& ba;這兩句&#xff0c;b與a實際上是一回事&#xff0c;表示的是同一塊內存。 std是系統的一個命名空間(有關命名空間可以參閱namespace_百度百科)…

Day21 奇異值分解(SVD)全面解析

一、奇異值分解概述 奇異值分解是線性代數中一個重要的矩陣分解方法&#xff0c;對于任何矩陣&#xff0c;無論是結構化數據轉化成的“樣本 * 特征”矩陣&#xff0c;還是天然以矩陣形式存在的圖像數據&#xff0c;都能進行等價的奇異值分解&#xff08;SVD&#xff09;。 二…

akshare爬蟲限制,pywencai頻繁升級個人做量化,穩定數據源和券商的選擇

做量化&#xff0c;數據和交易接口是策略和自動化交易的基石&#xff0c;而穩定的數據和快人一步的交易接口是個人做量化的催化劑。 之前寫過一篇文章&#xff1a;個人做量化常用的數據&#xff0c;多以爬蟲為主&#xff0c;最近akshare爬蟲限制&#xff0c;pywencai頻繁升級。…

數字簽名與證書

1. 數字簽名與證書 摘要算法用來實現完整性&#xff0c;能夠為數據生成獨一無二的“指紋”&#xff0c;常用的算法是 SHA-2&#xff1b;數字簽名是私鑰對摘要的加密&#xff0c;可以由公鑰解密后驗證&#xff0c;實現身份認證和不可否認&#xff1b;公鑰的分發需要使用數字證書…

Ubuntu22.04安裝顯卡驅動/卸載顯卡驅動

報錯 今日輸入nvidia-smi報錯,在安裝了535和550,包括560都沒辦法解決,但是又怕亂搞導致環境損壞,打算把顯卡卸載然后重新安裝系統默認推薦版本的顯卡驅動 qinqin:~$ nvidia-smi Failed to initialize NVML: Driver/library version mismatch NVML library version: 560.35卸載…

Web 架構之負載均衡全解析

文章目錄 一、引言二、思維導圖三、負載均衡的定義與作用定義作用1. 提高可用性2. 增強性能3. 實現擴展性 四、負載均衡類型硬件負載均衡代表設備優缺點 軟件負載均衡應用層負載均衡代表軟件優缺點 網絡層負載均衡代表軟件優缺點 五、負載均衡算法輪詢算法&#xff08;Round Ro…

linux下的Redis的編譯安裝與配置

配合做開發經常會用到redis&#xff0c;整理下編譯安裝配置過程&#xff0c;僅供參考&#xff01; --------------------------------------Redis的安裝與配置-------------------------------------- 下載 wget https://download.redis.io/releases/redis-6.2.6.tar.gz tar…

A2A大模型協議及Java示例

A2A大模型協議概述 1. 協議作用 A2A協議旨在解決以下問題&#xff1a; 數據交換&#xff1a;不同應用程序之間的數據格式可能不一致&#xff0c;A2A協議通過定義統一的接口和數據格式解決這一問題。模型調用&#xff1a;提供標準化的接口&#xff0c;使得外部應用可以輕松調…

關鍵點檢測--使用YOLOv8對Leeds Sports Pose(LSP)關鍵點檢測

目錄 1. Leeds Sports Pose數據集下載2. 數據集處理2.1 獲取標簽2.2 將圖像文件和標簽文件處理成YOLO能使用的格式 3. 用YOLOv8進行訓練3.1 訓練3.2 預測 1. Leeds Sports Pose數據集下載 從kaggle官網下載這個數據集&#xff0c;地址為link&#xff0c;下載好的數據集文件如下…

20250508在WIN10下使用移遠的4G模塊EC200A-CN直接上網

1、在WIN10/11下安裝驅動程序&#xff1a;Quectel_Windows_USB_DriverA_Customer_V1.1.13.zip 2、使用移遠的專用串口工具&#xff1a;QCOM_V1.8.2.7z QCOM_V1.8.2_win64.exe 3、配置串口UART42/COM42【移遠會自動生成連續三個串口&#xff0c;最小的那一個】 AT命令&#xf…

第J7周:ResNeXt解析

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 目標 具體實現 &#xff08;一&#xff09;環境 語言環境&#xff1a;Python 3.10 編 譯 器: PyCharm 框 架: Tensorflow &#xff08;二&#xff09;具體…

C++之類和對象:初始化列表,static成員,友元,const成員 ……

目錄 const成員函數&#xff1a; 前置和后置重載&#xff1a; 取地址及const取地址操作符重載&#xff1a; 初始化列表&#xff1a; explicit關鍵字&#xff1a; static成員&#xff1a; 友元&#xff1a; 友元函數&#xff1a; 友元類&#xff1a; 內部類&#xff1a…

uni-app 中的條件編譯與跨端兼容

uni-app 為了實現一套代碼編譯到多個平臺&#xff08;包括小程序&#xff0c;App&#xff0c;H5 等&#xff09;&#xff0c;引入了條件編譯機制。 通過條件編譯&#xff0c;我們可以針對不同的平臺編寫特定的代碼&#xff0c;從而實現跨端兼容。 一、條件編譯的作用 平臺差異…

Linux平臺下SSH 協議克隆Github遠程倉庫并配置密鑰

目錄 注意&#xff1a;先提前配置好SSH密鑰&#xff0c;然后再git clone 1. 檢查現有 SSH 密鑰 2. 生成新的 SSH 密鑰 3. 將 SSH 密鑰添加到 ssh-agent 4. 將公鑰添加到 GitHub 5. 測試 SSH 連接 6. 配置 Git 使用 SSH 注意&#xff1a;先提前配置好SSH密鑰&#xff0c;然…