【C++】模板2.0

最近學習了一些模板的知識,速寫本博客作為學習筆記,若有興趣,歡迎垂閱讀!

?1.非類型模板參數

?模板參數分類類型形參與非類型形參。

類型形參即:出現在模板參數列表中,跟在class或者typename之類的參數類型名稱。非類型形參,就是用一個常量作為類(函數)模板的一個參數,在類(函數)模板中可將該參數當成常量來使用。

ps:

  • 浮點數(C++20之前)、類對象以及字符串是不允許作為非類型模板參數的。
  • 非類型的模板參數必須在編譯期就能確認結果。
namespace hd
{// 定義一個模板類型的靜態數組template<class T, size_t N = 10>//N就是非類型模板參數class array{public:T& operator[](size_t index) { return _array[index]; }const T& operator[](size_t index)const { return _array[index]; }size_t size()const { return _size; }bool empty()const { return 0 == _size; }private:T _array[N];size_t _size;};
}int main()
{hd::array<int> ia;hd::array<char, 6> ca;return 0;
}

看到這個栗子,類模板參數N就是非類型模板參數。


庫里面也有使用非類型模板參數的栗子,比如類模板array?的設計就使用了非類型模板參數,看到N就是這個類模板的非類型模板參數:

array也是一個容器,底層其實就是一個靜態數組,關于其接口有興趣的話可以自行去查閱。不過這個容器比較雞肋吧,因為vector似乎更香。

當然,這個容器也有其優點:

  • 普通數組對于越界訪問的檢查是一種抽查,越界訪問了未必檢查得出來。但array對于越界訪問一查一個準,也許其底層實現加了斷言吧,例如其成員函數operator[]完全可以加斷言檢查是否越界。
  • 本容器對象一旦實例化,就不支持動態調整大小,因為其空間是靜態開辟的靜態數組。但是其空間是在棧區開辟的,而vector的空間是在堆區開辟的,是動態開辟的。

看到當非類型模板參數有缺省值的一些情況,實例化對象代碼寫法:

template<class T = int, size_t N = 10>
class a
{T _arr[N];
};
template<int N = 10>
class b
{int _arr[N];
};int main()
{a<> ap;b<> bp;return 0;
}

?C++20以后也支持如下寫法:

template<class T = int, size_t N = 10>
class a
{T _arr[N];
};
template<int N = 10>
class b
{int _arr[N];
};int main()
{a ap1;a<char> ap2;b bp;return 0;
}

2.模板的特化?

2.1.模板特化的概念?

通常情況下,使用模板可以實現一些與類型無關的代碼,但對于一些特殊類型的可能會得到一些 錯誤的結果,需要特殊處理,比如:實現了一個專門用來進行小于比較的函數模板

#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h) ,_m(m) ,_s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)
{return t1 < t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//結果正確cout << Less(&t1, &t2) << endl;//結果錯誤return 0;
}

?可以看到,Less絕對多數情況下都可以正常比較,但是在特殊場景下就得到錯誤的結果。上述示 例中,&t2指向的t2顯然小于&t1指向的t1對象,但是Less內部并沒有比較&t2和&t1指向的對象內 容,而比較的是&t1和&t2本身的值,這就無法達到預期而錯誤。

此時,就需要對模板進行特化。即:在原模板類的基礎上,針對特殊類型所進行特殊化的實現方 式。模板特化中分為函數模板特化類模板特化

2.2.函數模板特化

?函數模板的特化步驟:

1. 必須要先有一個基礎的函數模板

2. 關鍵字template后面接一對空的尖括號<>

3. 函數名后跟一對尖括號,尖括號中指定需要特化的類型

4. 函數形參表:?必須要和模板函數的基礎參數類型完全相同,如果不同編譯器可能會報一些奇 怪的錯誤。

#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h) ,_m(m) ,_s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)//注意這里const修飾的是引用,而不是修飾類型
{return t1 < t2;
}//Less函數模板的特化
template<>
bool Less<Time*>(Time* const & t1, Time* const & t2)
{return *t1 < *t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//走模板生成cout << Less(&t1, &t2) << endl;//調用特化之后的版本,而不走模板生成了return 0;
}


但是但是,?一般情況下如果函數模板遇到不能處理或者處理有誤的類型,為了實現簡單通常都是將該函數直接給出,而不是去特化函數模板,例如:

#include <iostream>
using namespace std;class Time
{int _h;int _m;int _s;
public:Time(int h, int m, int s):_h(h), _m(m), _s(s){}bool operator<(const Time& t)const{if (_h != t._h) return _h < t._h;else if (_m != t._m) return _m < t._m;return _s < t._s;}
};//專門比較小于的函數模板
template <class T>
bool Less(const T& t1, const T& t2)//注意這里const修飾的是引用,而不是修飾類型
{return t1 < t2;
}//現成函數(非函數模板特化)
bool Less(Time* t1, Time* t2)
{return *t1 < *t2;
}int main()
{Time t1(22, 22, 22);Time t2(11, 11, 11);cout << Less(t1, t2) << endl;//走模板生成cout << Less(&t1, &t2) << endl;//調用現成函數return 0;
}

直接將類型是Time*類型比較的函數給出,不是香噴噴嗎?何必走函數模板特化呢?所以函數模板不建議特化。

2.3.類模板特化?

?類模板特化分為全特化和半特化(偏特化)。

2.3.1.全特化

#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//類模板全特化,當第1個模板參數為int且第2個模板參數為char時,調用
template<>
class Data<int, char>
{
public:Data() { cout << "Data<int, char>" << endl; }
private:int _d1;char _d2;
};
int main()
{Data<int, int> d1;Data<int, char> d2;return 0;
}

?2.3.2.半特化(偏特化)

?偏特化:任何針對模版參數進一步進行條件限制設計的特化版本。偏特化有以下兩種表現方式:


  • 部分特化

將模板參數類表中的一部分參數特化。

#include <iostream>
using namespace std;
template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl; }
private:T1 _d1;T2 _d2;
};//類模板半特化,只要第2個類模板參數為int,調用
template<class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl; }
private:T1 _d1;int _d2;
};
int main()
{Data<int, int> d1;Data<int, char> d2;return 0;
}


    • 參數更進一步的限制

    偏特化并不僅僅是指特化部分參數,而是針對模板參數更進一步的條件限制所設計出來的一 個特化版本。

    #include <iostream>
    using namespace std;
    template<class T1, class T2>
    class Data
    {
    public:Data() { cout << "Data<T1, T2>" << endl; }
    private:T1 _d1;T2 _d2;
    };//類模板半特化,兩個參數偏特化為指針類型,只要2個類模板參數為指針,調用
    template<class T1, class T2>
    class Data<T1* , T2*>
    {
    public:Data() { cout << "Data<T1* , T2*>" << endl; }
    private:T1 _d1;T2 _d2;
    };
    int main()
    {Data<int, int> d1;Data<int*, char*> d2;return 0;
    }

    3.模板分離編譯?

    ?3.1.什么是分離編譯

    一個程序(項目)由若干個源文件共同實現,而每個源文件單獨編譯生成目標文件,最后將所有 目標文件鏈接起來形成單一的可執行文件的過程稱為分離編譯模式。

    3.2.模板的分離編譯

    模板是不推薦分離編譯的,也就是說模板不推薦聲明和定義分離到不同文件下,如果分離了會出現鏈接問題。例如:

    一個工程,有3個文件,分別是a.h、a.cpp、test.cpp。

    a.h:

    #pragma once//a類模板聲明
    template <class T>
    class a
    {T _tmp;
    public:a(const T& tmp = T());const T& get_tmp();
    };//Add函數模板聲明
    template<class T>
    T Add(const T& n1, const T& n2);

    a.cpp:

    #include "a.h"//a類模板定義
    template<class T>
    const T& a<T>::get_tmp()
    {return _tmp;
    }template<class T>
    a<T>::a(const T& tmp):_tmp(tmp)
    {}//Add函數模板定義
    template<class T>
    T Add(const T& n1, const T& n2)
    {return n1 + n2;
    }

    ?test.cpp:

    #include <iostream>
    using namespace std;
    #include "a.h"int main()
    {a<int> x(10);cout << x.get_tmp() << endl;cout << Add(1, 2) << endl;return 0;
    }
    

    這3個文件中有2個模板,聲明和定義都分離了。編譯會出現問題:


    如何解決??

    ?2個辦法:

    1. 將聲明和定義放到一個文件 "xxx.hpp" 里面或者xxx.h其實也是可以的,也就是說模板的聲明和定義不要分離到不同文件。推薦使用這種辦法。
    2. 模板定義的位置顯式實例化。這種方法不實用,不推薦使用,如下:

    a.cpp:

    #include "a.h"//a類模板定義
    template<class T>
    const T& a<T>::get_tmp()
    {return _tmp;
    }template<class T>
    a<T>::a(const T& tmp):_tmp(tmp)
    {}//Add函數模板定義
    template<class T>
    T Add(const T& n1, const T& n2)
    {return n1 + n2;
    }//顯示實例化
    template
    class a<int>;//顯示實例化
    template
    int Add(const int& n1, const int& n2);


    ?模板聲明和定義分離到不同文件下,且不在模板定義的位置顯示實例化的話,會出現鏈接錯誤的原因的話,感興趣可以自己去查閱哈,我就不介紹了。

    感謝閱讀,歡迎斧正!

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

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

    相關文章

    目標檢測中的損失函數(二) | BIoU RIoU α-IoU

    BIoU來自發表在2018年CVPR上的文章&#xff1a;《Improving Object Localization With Fitness NMS and Bounded IoU Loss》 論文針對現有目標檢測方法只關注“足夠好”的定位&#xff0c;而非“最優”的框&#xff0c;提出了一種考慮定位質量的NMS策略和BIoU loss。 這里不贅…

    如何在 Amazon EC2 上部署 Java(Spring Boot 版)

    讓我們學習如何將 Java Spring Boot Web 服務器部署到 Amazon EC2。每月只需 3 美元。 使用 Azure&#xff0c;您可能不知道要花費多少錢。 Spring Boot 項目示例 在本教程中&#xff0c;我們將重點介紹如何將 Java Spring Boot 服務器部署到 Amazon EC2&#xff0c;因此我們不…

    Git常用命令分類匯總

    Git常用命令分類匯總 一、基礎操作 初始化倉庫git init添加文件到暫存區git add file_name # 添加單個文件 git add . # 添加所有修改提交更改git commit -m "提交描述"查看倉庫狀態git status二、分支管理 創建/切換分支git branch branch_name …

    mysql——基礎知識

    關鍵字大小寫不敏感 查看表結構中的 desc describe 描述 降序中的 desc descend 1. 數據庫的操作 1. 創建數據庫 create database 數據庫名;為防止創建的數據庫重復 CREATE DATABASE IF NOT EXISTS 數據庫名;手動設置數據庫采用的字符集 character set 字符集名;chars…

    Redis 哨兵與集群腦裂問題詳解及解決方案

    Redis 哨兵與集群腦裂問題詳解及解決方案 本文將深入探討Redis在哨兵模式和集群模式下可能出現的腦裂問題&#xff0c;包括其發生場景、原因以及有效的解決策略。同時&#xff0c;我們還將提供相應的代碼示例和配置方案來幫助讀者理解和實施。 一、腦裂問題概述 腦裂&#x…

    國內網絡設備廠商名單(List of Domestic Network Equipment Manufacturers)

    國內網絡設備廠商名單 運維工程師必須廣泛熟悉國內外各大廠商的設備&#xff0c;深入掌握其應用場景、功能特點及優勢。這不僅有助于在故障排查時迅速定位問題&#xff0c;還能在系統設計、優化與升級中做出更合理的決策。對設備特性的精準把握&#xff0c;能夠顯著提升運維效…

    2、SpringAI接入ChatGPT與微服務整合

    2、SpringAI接入ChatGPT與微服務整合 小薛博客AI 大模型資料 1、SpringAI簡介 https://spring.io/projects/spring-ai Spring AI是一個人工智能工程的應用框架。其目標是將Spring生態系統的設計原則&#xff08;如可移植性和模塊化設計&#xff09;應用于人工智能領域&#…

    基于ubuntu24.10安裝NACOS2.5.1的簡介

    基于ubuntu24.10安裝NACOS2.5.1的簡介 官方網站地址&#xff1a; https://nacos.io 可訪問nacos站點 https://nacos.io/zh-cn/ 2025年04月記錄發布 V2.5.1 版本 一、環境預準備 64 bit JDK 1.8&#xff1b; sudo apt update sudo apt install openjdk-8-jdk sudo apt upda…

    神經網絡:從基礎到應用,開啟智能時代的大門

    在當今數字化時代&#xff0c;神經網絡已經成為人工智能領域最熱門的技術之一。從語音識別到圖像分類&#xff0c;從自然語言處理到自動駕駛&#xff0c;神經網絡的應用無處不在。它不僅改變了我們的生活方式&#xff0c;還為各個行業帶來了前所未有的變革。本文將帶你深入了解…

    [k8s實戰]Containerd 1.7.2 離線安裝與配置全指南(生產級優化)

    [k8s實戰]Containerd 1.7.2 離線安裝與配置全指南&#xff08;生產級優化&#xff09; 摘要&#xff1a;本文詳細講解在無外網環境下部署 Containerd 1.7.2 容器運行時的完整流程&#xff0c;涵蓋二進制包安裝、私有鏡像倉庫配置、Systemd服務集成等關鍵步驟&#xff0c;并提供…

    【CPU】結合RISC-V CPU架構回答中斷系統的7個問題(個人草稿)

    結合RISC-V CPU架構對中斷系統七個關鍵問題的詳細解析&#xff0c;按照由淺入深的結構進行說明&#xff1a; 一、中斷請求機制&#xff08;問題①&#xff09; 硬件基礎&#xff1a; RISC-V通過CLINT&#xff08;Core Local Interrupter&#xff09;和PLIC&#xff08;Platfor…

    [密碼學實戰]國密算法面試題解析及應用

    以下是密碼學領域常見的面試題及其詳細解析,涵蓋基礎理論、算法實現與應用場景,幫助系統化備戰技術面試 一、基礎概念類 1. 密碼學的主要目標是什么? 答案: 確保數據的機密性(加密防止竊聽)、完整性(哈希校驗防篡改)、認證性(數字簽名驗證身份)和不可否認性(簽名防…

    Spring Boot 實現 Excel 導出功能(支持前端下載 + 文件流)

    &#x1f9e0; 一、為什么用 EasyExcel&#xff1f; 在 Java 開發中&#xff0c;操作 Excel 的框架主要有&#xff1a; Apache POI&#xff08;經典但慢、內存占用大&#xff09; JXL&#xff08;老舊不維護&#xff09; Alibaba EasyExcel&#xff08;阿里出品&#xff0c;…

    【論文速遞】2025年06周 (Robotics/Embodied AI/LLM)

    目錄 SMOLLM2&#xff1a;當Smol變得大 - 以數據為中心的小語言模型英文摘要中文摘要 OmniHuman-1&#xff1a;重新考慮一階段的人類動畫模型的擴展英文摘要中文摘要 S1&#xff1a;簡單的測試時間縮放英文摘要中文摘要 直接對齊算法間的差異日漸模糊英文摘要中文摘要 VideoJAM…

    學習深度學習是否要先學習機器學習?工程師的路徑選擇策略

    深度學習與機器學習的關系&#xff0c;如同摩天大樓與地基——前者是后者的高階延伸&#xff0c;但能否繞過地基直接造樓&#xff1f;本文從技術本質、學習曲線、應用場景三個維度剖析這一關鍵問題。 一、技術血脈的承繼關系 概念體系同源&#xff1a; 損失函數、梯度下降、過擬…

    開始放飛之先搞個VSCode

    文章目錄 開始放飛之先搞個VSCode重要提醒安裝VSCode下載MinGW-w64回到VSCode中去VSCode原生調試鍵盤問題遺留問題參考文獻 開始放飛之先搞個VSCode 突然發現自己的新臺式機上面連個像樣的編程環境都沒有&#xff0c;全是游戲了&#xff01;&#xff01;&#xff01;&#xff…

    【2025“華中杯”大學生數學建模挑戰賽】選題分析 A題 詳細解題思路

    目錄 2025“華中杯”大學生數學建模挑戰賽選題分析A題&#xff1a;晶硅片產銷策略優化B題&#xff1a;校園共享單車的調度與維護問題C題&#xff1a;就業狀態分析與預測D題&#xff1a;患者院內轉運不良事件的分析與預測 A 題 晶硅片產銷策略優化問題 1&#xff1a;月利潤計算模…

    YOLO11改進,尺度動態損失函數Scale-based Dynamic Loss,減少標簽不準確對損失函數穩定性的影響

    在目標檢測領域,標簽噪聲與尺度敏感問題始終是制約模型性能提升的"阿喀琉斯之踵"。2025年CVPR最佳論文提出的尺度動態損失函數(Scale-based Dynamic Loss, SDL),通過構建自適應損失調節機制,不僅實現了對YOLOv11檢測精度的指數級提升,更重新定義了損失函數的設…

    緩存 --- 內存緩存 or 分布式緩存

    緩存 --- 內存緩存 or 分布式緩存 內存緩存&#xff08;In-Memory Cache&#xff09;分布式緩存&#xff08;Distributed Cache&#xff09;內存緩存 vs 分布式緩存 內存緩存和分布式緩存是兩種常見的緩存策略&#xff0c;它們在存儲位置、訪問速度和適用場景上有所不同。下面分…

    Python+CoppeliaSim+ZMQ remote API控制機器人跳舞

    這是一個使用Python和CoppeliaSim&#xff08;V-REP&#xff09;控制ASTI人型機器人進行舞蹈動作的演示項目。 項目描述 本項目展示了如何使用Python通過ZeroMQ遠程API與CoppeliaSim仿真環境進行交互&#xff0c;控制ASTI人型機器人執行預定義的舞蹈動作序列。項目包含完整的機…