C++ | 可變模板參數

1. 為什么需要可變模板參數?

在C++11之前,若想實現一個接受任意數量參數的函數,只能依賴va_list等C風格可變參數,但這種方式類型不安全且難以調試。例如printf函數:

printf("%d %f %s", 10, 3.14, "hello"); // 若格式字符串與參數類型不匹配,直接崩潰!

可變模板參數的誕生解決了這一問題:類型安全?+?編譯期展開。它是std::make_sharedstd::tuple等工具的實現基石!


2. 基礎語法:聲明與展開

2.1 聲明參數包

使用typename...定義模板參數包,函數參數中使用Args... args接收實參:

template <typename... Args>
void log(Args... args); // Args: 類型參數包; args: 函數參數包
2.2 混合固定參數與可變參數
template <typename T, typename... Args>
void process(T first, Args... rest); // first處理第一個參數,rest處理剩余參數

3. 參數包展開的兩種核心方式

3.1 遞歸展開(經典方法)

通過遞歸模板函數逐步“剝開”參數包,需定義遞歸終止條件。

示例:遞歸打印所有參數

// 終止函數:無參數時結束遞歸
void print() { std::cout << "End\n"; 
}// 遞歸函數模板
template <typename T, typename... Args>
void print(T first, Args... rest) {std::cout << first << " ";print(rest...); // 遞歸調用,rest參數包被展開
}print(42, "Hello", 3.14); // 輸出:42 Hello 3.14 End

關鍵點:遞歸調用時,參數包rest...會被編譯器自動展開為下一個調用的參數列表。


3.2 折疊表達式(C++17起,更簡潔!)

折疊表達式(Fold Expression)允許用簡潔的語法對參數包進行展開操作,支持所有二元運算符。

示例1:求和所有參數

template <typename... Args>
auto sum(Args... args) {return (args + ...); // 等價于 args1 + args2 + ... + argsN
}std::cout << sum(1, 2, 3, 4); // 輸出:10

示例2:打印所有參數(逗號分隔)

template <typename... Args>
void print(Args&&... args) {(std::cout << ... << args) << "\n"; // 折疊輸出,展開為 ((cout << arg1) << arg2) << ...
}print("Age:", 25, ", Score:", 99.5); // 輸出:Age:25, Score:99.5

優勢:無需遞歸,代碼簡潔,編譯效率更高!


4. 類模板中的可變參數

可變模板參數在類模板中同樣大放異彩,例如實現一個簡單的元組(std::tuple的簡化版):

template <typename... Types>
class Tuple;// 遞歸繼承特化:通過繼承展開參數包
template <typename T, typename... Rest>
class Tuple<T, Rest...> : private Tuple<Rest...> {
public:T value;Tuple(T v, Rest... args) : value(v), Tuple<Rest...>(args...) {}
};// 基類:空參數包時終止
template <>
class Tuple<> {};// 使用
Tuple<int, std::string, double> t(10, "Test", 3.14);

解析:通過遞歸繼承,每個Tuple層保存一個值,并繼承剩余參數的Tuple基類,最終構造出一個包含所有數據的結構。


5. 實用技巧與常見操作

5.1 獲取參數包大小

使用sizeof...運算符獲取參數包中的參數數量:

template <typename... Args>
void logSize(Args... args) {std::cout << "參數數量:" << sizeof...(Args) << "\n";
}logSize(1, "two", 3.0); // 輸出:參數數量:3
5.2 完美轉發參數包

結合std::forward實現完美轉發,保留參數的左值/右值特性:

template <typename... Args>
void wrapper(Args&&... args) {// 將參數包完美轉發給目標函數targetFunc(std::forward<Args>(args)...);
}

6. 實際應用場景

  1. 工廠函數:如std::make_shared<T>(args...),根據參數構造對象。

  2. 格式化日志:接受任意類型和數量的參數,生成日志字符串。

  3. 元編程工具:實現std::tuplestd::variant等容器。

  4. 委托與信號槽:處理不同數量和類型的回調參數。


7. 注意事項

  • 遞歸終止條件:遞歸展開時務必定義終止函數,否則編譯失敗。

  • 性能開銷:遞歸展開可能增加編譯時間,折疊表達式更高效。

  • 參數順序:混合固定參數和可變參數時,注意參數順序。


總結

可變模板參數為C++泛型編程打開了全新的大門,結合折疊表達式和完美轉發,可以優雅地處理任意數量和類型的參數。它是現代C++庫開發的基石,熟練掌握這一特性,你將能寫出更靈活、更強大的通用代碼!

動手建議:嘗試用可變模板參數實現一個類型安全的格式化函數(類似Python的format),支持format("{} + {} = {}", 2, 3, 5)的輸出。

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

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

相關文章

【機器學習】每日一講-樸素貝葉斯公式

文章目錄 **一、樸素貝葉斯公式詳解****1. 貝葉斯定理基礎****2. 從貝葉斯定理到分類任務****3. 特征獨立性假設****4. 條件概率的估計** **二、在AI領域的作用****1. 文本分類與自然語言處理&#xff08;NLP&#xff09;****2. 推薦系統****3. 醫療與生物信息學****4. 實時監控…

AI Agents系列之AI代理的類型

在本文中,我們將探討不同類型的 AI 代理,包括它們的實現、實際應用、優勢和局限性。從簡單反射代理到多代理系統,我們將了解這些模型如何推動自動化、決策制定和智能問題解決。 文章目錄 1. AI代理的類型1.1 簡單反射代理1.1.1 實現**1.1.2 優勢****1.1.3 局限性**1.2 基于…

C# --- IEnumerable 和 IEnumerator

C# --- IEnumerable 和 IEnumerator IEnumerableIEnumeratorIEnumerable 和 IEnumerator 的作用手動實現 IEnumerableIEnumerable vs. IQueryable為什么有了ienumerator還需要ienumerable IEnumerable 在C#中&#xff0c;IEnumerable 是一個核心接口&#xff0c;用于表示一個可…

鏡舟科技助力某大型電網企業破解數據架構升級難題,打造國產化湖倉標桿

在 “十四五” 規劃全面推進國產化替代的背景下&#xff0c;某大型電網企業聯合鏡舟科技與騰訊云&#xff0c;基于全球領先的開源分析型數據庫 StarRocks 及騰訊 TBDS 大數據平臺&#xff0c;構建電力行業國產化湖倉一體架構。該項目實現 PB 級電力數據的統一管理&#xff0c;為…

Spark-SQL核心編程3

數據加載與保存 通用方式&#xff1a; SparkSQL 提供了通用的保存數據和數據加載的方式。這里的通用指的是使用相同的API&#xff0c;根據不同的參數讀取和保存不同格式的數據&#xff0c;SparkSQL 默認讀取和保存的文件格式為parquet 數據加載方法&#xff1a; spark.read.lo…

使用HTML + CSS + JS,編寫一個臺球追分計分器

目錄 一.代碼 二.效果展示 三.該計分器的優點 一.代碼 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

LLM小白自學筆記:1.兩種指令微調

一、LoRA 簡單來說&#xff0c;LoRA不直接調整個大模型的全部參數&#xff08;那樣太費資源&#xff09;&#xff0c;而是在模型的某些層&#xff08;通常是注意力層&#xff09;加個“旁路”——兩個小的矩陣&#xff08;低秩矩陣&#xff09;。訓練時只更新這倆小矩陣&#x…

2026《數據結構》考研復習筆記一(C++基礎知識)

C基礎知識復習 一、數據類型二、修飾符和運算符三、Lambda函數和表達式四、數學函數五、字符串六、結構體 一、數據類型 1.1基本類型 基本類型 描述 字節&#xff08;位數&#xff09; 范圍 char 字符類型&#xff0c;存儲ASCLL字符 1&#xff08;8位&#xff09; -128…

基于骨骼識別的危險動作報警分析系統

基于骨骼識別的危險動作報警分析系統 【包含內容】 【一】項目提供完整源代碼及詳細注釋 【二】系統設計思路與實現說明 【三】基于骨骼識別算法的實時危險行為預警方案 【技術棧】 ①&#xff1a;系統環境&#xff1a;Windows 10/11、macOS Ventura、Ubuntu 20.04 ②&#x…

【雙指針】四數之和(medium)

四數之和&#xff08;medium&#xff09; 題?描述&#xff1a;解法&#xff08;排序 雙指針&#xff09;算法思路&#xff1a; C 算法代碼&#xff1a;Java 算法代碼&#xff1a; 題?鏈接&#xff1a;18. 四數之和 題?描述&#xff1a; 給你?個由 n 個整數組成的數組 num…

Flask+Influxdb+grafna構建電腦性能實時監控系統

Influx下載地址&#xff0c;這里下載了以下版本influxdb-1.8.5_windows_amd64.zip 運行前需要先啟動Influx數據庫&#xff1a; 管理員方式運行cmd->F:->cd F:\influxdb\influxdb-1.8.5-1->influxd -config influxdb.conf&#xff0c;以influxdb.conf配置文件啟動數…

如何在Keil中配置國民技術N32G系列MCU開發環境

如何在Keil及Jlink中搭建國民技術N32G系列MCU開發環境 根據自己的MCU型號&#xff08;我這里的型號是N32G452REL7&#xff09;訪問國民技術官網&#xff0c;依次從N32G通用MCU-技術資源-固件和軟件-軟件開發套件&#xff0c;獲取對應MCU型號的SDK&#xff0c;也可點擊這里從網盤…

微軟承認Win11出現極端錯誤,只能強制關機或重裝系統

最近&#xff0c;不少使用 Windows 11 的用戶反映&#xff0c;在系統更新后&#xff0c;“Windows Hello”突然失效&#xff0c;原本便捷的人臉識別和PIN登錄功能統統無法使用。更糟的是&#xff0c;有人在重置系統后直接被擋在系統門外&#xff0c;這讓人不禁發問&#xff1a;…

【android bluetooth 協議分析 02】【bluetooth hal 層詳解 1】【uart 介紹】

一、什么是 UART&#xff1f; UART&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09; 是一種 串行通信協議&#xff0c;它的特點是通信時不需要專門的時鐘信號&#xff08;叫做“異步”通信&#xff09;&#xff0c;常用于兩個設備之間的簡單數據通信&…

天元證券|奶粉行業結構性回暖 乳企競速全齡化、國際化

在過去幾年中&#xff0c;中國嬰配粉市場經歷了量價齊增&#xff0c;量減價增&#xff0c;量減價減的三個周期。歷經多年行業深度洗牌與競爭格局重塑&#xff0c;2024年中國嬰配粉市場回暖態勢愈發清晰可辨。 日前&#xff0c;包括中國飛鶴、澳優、健合集團在內的多家奶粉股披露…

第3.1節 調用鏈路分析簡介

調用鏈路&#xff08;Call Chain / Call Path&#xff09; 是程序在執行過程中&#xff0c;按照調用順序形成的函數、模塊或組件之間的依賴關系鏈條&#xff0c;完整記錄了從程序入口到當前執行點的動態調用路徑。它反映了代碼執行的邏輯流程&#xff0c;是分析程序行為、調試問…

System.Security.Cryptography.CryptographicException“填充無效,無法被移除。”

這個異常通常發生在以下幾種情況&#xff1a; 1.密文損壞&#xff1a;密文在傳輸或存儲過程中被篡改或損壞。 2.密鑰不匹配&#xff1a;用于解密的密鑰與加密時使用的密鑰不同。 3.填充模式不匹配&#xff1a;加密時使用的填充模式與解密時指定的填充模式不一致。 4.使用了不正…

【網絡入侵檢測】Suricata之數據包內容匹配

【作者主頁】只道當時是尋常 【專欄介紹】入侵檢測。專注網絡、主機安全&#xff0c;歡迎關注與評論。 1. 概要 本文詳細介紹了網絡入侵檢測系統&#xff08;如 Suricata&#xff09;中用于檢查數據包或流有效載荷的 Payload 關鍵字。content 用于匹配數據包內容&#xff0c;默…

Spring Boot 整合 Redis 實現點贊功能:從基礎到實踐

在當今互聯網應用開發中&#xff0c;點贊功能幾乎成為了各類內容平臺的標配。它不僅能增加用戶與內容之間的互動&#xff0c;還能直觀地反映內容的受歡迎程度。本文將詳細介紹如何使用 Spring Boot 整合 Redis 來實現一個簡單的文章點贊功能&#xff0c;讓你輕松掌握這一實用技…

openGauss DataVec + Dify,快速搭建你的智能助手平臺

在當今數字化和智能化的時代&#xff0c;大語言模型&#xff08;LLM&#xff09;的應用正以前所未有的速度改變著各個領域的工作方式和用戶體驗。Dify 作為一個開源的大語言模型應用開發平臺&#xff0c;為開發者們提供了便捷且強大的工具&#xff0c;助力構建從基礎智能體到復…