C++:非類型模板參數,模板特化以及模板的分離編譯

目錄

一、前言

二、非類型模板參數

三、模板的特化

3.1 類模板特化

3.11 全特化

3.12 偏特化

3.2 函數模板特化

3.3 注意

四、模板的分離編譯


一、前言

前面的文章梳理了模板初階的一些用法,在后面梳理了STL的一些容器的用法后,下面將用到含有STL的模板部分知識進行梳理。

那么首先我們先看下面的代碼:

template<class Container>
void Print(const Container& con)
{Container::const_iterator it=con.begin();while(it != con.end()){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);Print(v);
}

這里我們首先實例化了一個vector<int>的對象v,然后插入兩個數據,最后寫了一個打印函數來打印有迭代器的容器的數據,但這里我們運行后出現了上圖的錯誤,這個錯誤出現的原因是第四行我們使用模板導致的,因為在我們使用模板進行定義時,在沒有對它進行實例化前編譯器無法識別,在沒有實例化時,Container::const_iterator這一個定義,編譯器無法識別,因為這一句話既可以當作類型,也可以當作變量/對象,雖然作為變量時加上后面的it是不對的,但編譯器無法在沒有其他任何操作前正確識別,所以報錯了。那么我們想要解決這個問題只要提前告訴編譯器這是類型就可以了,這里用到了定義模板參數時的另一個與class同等效果的typename,typename在模板中與class唯一的不同就在于將typename提前可以告訴編譯器這句話是代表類型,這樣編譯器就會在識別時確定這是類型,就不會報錯了。

template<class Container>
void Print(const Container& con)
{typename Container::const_iterator it=con.begin();while(it != con.end()){cout << *it << " ";it++;}cout << endl;
}int main()
{vector<int> v;v.push_back(1);v.push_back(2);Print(v);
}

類似上面這樣的只要傳過來的容器含有模板參數(vector<T>這樣的也是)都要在前面加typename

注意:無論定義模板參數時是class還是typename,遇到上面這種模板參數來定義時都要在定義前加typename

二、非類型模板參數

首先我們知道模板的使用格式是

template<class (模板參數),? ? ...>

那么非類型模板參數就是在模板參數的定義中用常量來代表模板參數。

下面我們寫了一個靜態棧的代碼:

#define N 10
template<class T>
class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int> st1;Stack<int> st2;
}

如果此時我們需要st1的大小為10,st2的大小為100,如何做?此時我們發現一個靜態棧已經無法實現了,那么此時只要我們用到非類型模板參數加一個非類型模板參數即可:

template<class T,size_t N>
class Stack
{
private:T _a[N];int _top;
};
int main()
{Stack<int,10> st1;Stack<int,100> st2;
}

上述代碼所示:我們在模板參數中加了一個非類型模板參數,這樣我們在實例化時只要填我們需要的常量時就可以了。

注意:
1. 浮點數、類對象以及字符串是不允許作為非類型模板參數的。
2. 非類型的模板參數必須在編譯期就能確認結果。

三、模板的特化

在實際應用中會遇到一些普通模板無法實現的情況,需要特殊處理,那么此時就可以使用模板的特化來特殊處理。(這里以梳理用法為主)

模板的特化分為函數模板特化和類模板特化

3.1 類模板特化

3.11 全特化

全特化就是將所有模板參數特殊化,格式如下:

template<>

class A<類型,類型...>

{};

全特化將<>中的所有模板參數去掉,將我們需要特化的類型放到類名后面的<>中

我們看下面的代碼:

//沒有特化
template<class T1,class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 d1;T2 d2;
};
//全特化
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;
}

這串代碼中上面是沒有特化的,下面是全特化,全特化這里我們需要int和char型,所以將類型寫到Data后即可,那么此時我們只有在調用時以Data<int, char> d2 ;這樣的格式才可以使用全特化,<int,int>在這里只能調用沒有特化類

3.12 偏特化

偏特化就是特化部分模板參數或進一步限制模板參數。

template<class T1,class T2>
class Data
{
public:Data(){cout << "Data<T1, T2>" << endl;}
private:T1 d1;T2 d2;
};
//偏特化1:特化部分模板參數
template<class T1>
class Data<T1, int>
{
public:Data(){cout << "Data<T1, int>" << endl;}
private:T1 d1;int d2;
};
//偏特化2:進一步限制模板參數
template<class T1, class T2>
class Data<T1*,T2*>
{
public:Data(){cout << "Data<T1*, T2*>" << endl;}
private:T1* d1;T2* d2;
};

偏特化有兩種,偏特化1:特化部分模板參數和偏特化2:進一步限制模板參數,它們代表著兩種偏特化形式:

偏特化1:將部分模板參數特化,上述代碼中我們可以看到保留了一個模板參數T1,特化了一個模板參數成int。

偏特化2:這里保留了所有的模板參數,但將所有的模板參數進一步進行限制.。例如上述代碼中將所有的模板參數限制為指針,也就是說只要參數都是指針類型的都會去調用這個模板類。

3.2 函數模板特化

函數模板只有全特化

#include<stdbool.h>
//沒有特化
template<class T>
bool Less(T a, T b)
{return a < b;
}
//全特化
template<>
bool Less<Date*>(Date* a, Date* b)
{return *a < *b;
}int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl;
}

上述代碼中先寫了一個Less沒有特化的比較,當遇到像Date* p1=&d1這樣的變量時再使用普適模板就無法比較了,所以需要寫一個Less模板的特化將Date*單寫出來

bool Less(Date* a, Date* b)
{return *a < *b;
}

上述代碼中沒有使用模板但依然可以正常比較,大部分的函數模板都可以被不適用模板的函數代替,所以此時再使用模板就會降低運行效率,因此函數模板的特化在此時沒有必要使用。

3.3 注意

無論是類模板特化還是函數模板特化,在使用特化時必須要有普通模板的存在,不然會報錯。

四、模板的分離編譯

我們有時會為了方便將函數的聲明和定義分離,將聲明放到頭文件中,定義放到源文件中,但如果使用了模板后那么這個函數就不可以與普通函數一樣放到兩個文件中。

我們看下面代碼:

//Stack.h
namespace lbs
{template<class T, class container = vector<int>>class stack{public:void push(const T& val);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}private:container _con;};
//-------------Stack.cpp---------------------------
namespace lbs
{template<class T, class container>void stack<T, container>::push(const T& val){_con.push_back(val);}template<class T, class container>void stack<T, container>::pop(){_con.pop_back();}//template class stack<int>;
}
//-----------------test.cpp-------------------------
int main()
{lbs::stack<int> st1;st1.push(1);st1.pop();
}

上述代碼中,我們在Stack.h文件中我們寫了一個stack類,其中push和pop進行了聲明與定義分離,size和top沒有進行分離,此時我們運行后在定義的push和pop處會報錯。

這里出現錯誤的原因就是在鏈接過程中編譯器無法正確識別類模板的實例化,因為這里出現了模板參數,而模板參數在改函數沒有被調用時不會實例化,所以鏈接階段沒有進行實例化,那么此時在Stack,cpp文件中的定義就無法正常進行。

想要正確分離有兩種方法:

1、較為麻煩(不推薦)

在Stack.cpp文件中加上template class stack<類型>;這一句話

在定義所在文件中加上這一句話就代表提前告訴編譯器要對此類型進行實例化,但麻煩的地方在于每此用新的類型時都要加上這一句話并填上對應的類型

2、較為簡單(推薦)

namespace lbs
{template<class T, class container = vector<int>>class stack{public:void push(const T& val);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}private:container _con;};template<class T, class container>void stack<T, container>::push(const T& val){_con.push_back(val);}template<class T, class container>void stack<T, container>::pop(){_con.pop_back();}
}

將定義放到聲明的下方,此時聲明與定義分離

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

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

相關文章

【Qt 學習之路】Qt Android開發環境搭建:Ubuntu的Vmware虛擬機中的踩坑實錄

文章目錄1、簡介2、虛擬機內USB設備識別難題2.1、正確連接手機2.2、打開USB相關配置2.3、打開虛擬機中的手機設備3、Gradle下載速度緩慢之困3.1、下載 Gradle 鏡像3.2、安放鏡像位置3.3、修改項目中的gradle路徑1、簡介 許久未曾使用Qt進行Android開發&#xff0c;今日在Ubunt…

MySQL中使用group_concat遇到的問題及解決

在使用group_concat的過程中遇到個問題&#xff0c;這里記錄一下&#xff1a;在MySQL中有個配置參數group_concat_max_len&#xff0c;它會限制使用group_concat返回的最大字符串長度&#xff0c;默認是1024。 查詢group_concat_max_len大小&#xff1a; show variables like…

高性能小型爬蟲語言與代碼示例

高性能小型爬蟲現在有哪幾種新興語言可以選擇。我看到了很多關于爬蟲框架的信息&#xff0c;特別是使用Go語言和Node.js的框架。Go語言方面有Kaola1和Katana2這兩個框架。Kaola被描述為高性能的Go語言爬蟲框架&#xff0c;輕量級且強大&#xff0c;提供靈活配置選項。 Node.js…

【PTA數據結構 | C語言版】在順序表 list 中查找元素 x

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 請編寫程序&#xff0c;將 n 個整數存入順序表&#xff0c;對任一給定整數 x&#xff0c;查找其在順序表中的位置。 輸入格式&#xff1a; 輸入首先在第一行給出正整數 n&#xff08;≤10^4 &#…

claude code-- 基于Claude 4 模型的智能編程工具,重塑你的編程體驗

文章目錄0.前言1.安裝nodejs2.使用指南3.快速上手4.總結0.前言 最近的這個claudecode非常的火&#xff0c;因為可能是這個cursoe定價的一些原因吧&#xff0c;我是聽其他的這個大佬說的&#xff0c;因為這個cursor其實我就是最開始的使用用過一下&#xff0c;現在基本上不使用…

HTTP API 身份認證

互聯網系統通常需要根據用戶身份決定是否有資源的訪問權限&#xff0c;這就需要對用戶進行身份認證&#xff08;Authentication&#xff09;&#xff0c;驗證用戶所聲稱的身份。驗證手段通常是驗證只有用戶知道或擁有的東西&#xff0c;比如密碼、手機號、指紋等。 基于瀏覽器…

Python畢業設計232—基于python+Django+vue的圖書管理系統(源代碼+數據庫)

畢設所有選題&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于pythonDjangovue的圖書管理系統(源代碼數據庫)232 一、系統介紹 本項目前后端分離&#xff0c;分為用戶、管理員兩種角色 1、用戶&#xff1a; 注冊、登錄、新聞資訊、圖書信…

Koa+Puppeteer爬蟲教程頁面設計

當我使用Koa作為web服務器&#xff0c;Puppeteer作為爬蟲工具來編寫一個簡單的爬蟲教程時&#xff0c;發生了戲劇性的一幕。 下面我將創建一個完整的Koa Puppeteer爬蟲教程頁面&#xff0c;包含代碼示例、執行演示和詳細說明。設計思路 左側為教程內容區域右側為實時爬蟲演示區…

云成本優化完整指南:從理論到實踐的全方位解決方案

目錄 引言:云成本管理的重要性云成本優化的核心原則成本分析與監控體系立即行動的快速優化策略中期架構優化方案長期成本治理體系多云環境成本管理實施路線圖與最佳實踐案例研究與效果評估總結與展望引言:云成本管理的重要性 {#引言} 在數字化轉型的浪潮中,

計算機學科專業基礎綜合(408)四門核心課程的知識點總結

一、數據結構&#xff08;Data Structure&#xff09; 數據結構是 “如何高效組織和處理數據” 的學科&#xff0c;核心是邏輯結構&#xff08;數據間的關系&#xff09;和物理結構&#xff08;數據在內存中的存儲方式&#xff09;&#xff0c;以及基于這兩種結構的操作算法。 …

JVM GC長暫停問題排查

JVM GC長暫停問題排查 現象 名詞&#xff1a;GC 垃圾回收&#xff08;Garbage Collection&#xff09;分類 計算機科學 在高并發下&#xff0c;Java程序的GC問題屬于很典型的一類問題&#xff0c;帶來的影響往往會被進一步放大。不管是「GC頻率過快」還是「GC耗時太長」&#x…

前端開發中的難題及解決方案

在前端開發過程中&#xff0c;開發者常常會遇到各種棘手的問題&#xff0c;這些問題不僅影響開發效率&#xff0c;還可能對產品質量和用戶體驗造成負面影響。下面詳細探討常見難題及有效解決方案。一、跨瀏覽器兼容性問題難題表現&#xff1a;不同瀏覽器&#xff08;如 Chrome、…

halcon 求一個tuple的極值點

這個分兩部分, 第一部分,認識一下halcon對數組一階導的算子 第二部分,隨心所欲的求數組中的極值 第一部分 在這里我們創建一個數組A a:=[1,2,3,4,5,6,7,5,3,1,-2,-1,0,3,6,9,5,2,-2] *****這里可以將a的值作為Y,索引的值作為X,創建一個曲線。 create_funct_1d_array (a…

SpringAI學習筆記-Chat簡單示例

Spring AI的內容太多太多。Chat是其中的一部分&#xff0c;也是其中非常重要、非常基礎的一部分&#xff0c;所以適合用來入門。 Chat API主要涉及幾個概念&#xff1a; Client&#xff1a;代表各模型的客戶端&#xff0c;負責請求和響應。Prompt&#xff1a;請求的最外層封裝&…

AI與物聯網(IoT)的融合

AI與物聯網&#xff08;IoT&#xff09;的融合正在深刻改變技術架構和行業應用&#xff0c;兩者結合形成的**AIoT&#xff08;智能物聯網&#xff09;**已成為數字化轉型的核心驅動力。以下是關鍵維度的分析&#xff1a;一、技術融合的底層邏輯 數據閉環的構建 IoT&#xff1a;…

Java----Maven

4. Maven 4.1 Maven 坐標組成部分&#xff1a;Maven 坐標由groupId、artifactId、version三部分組成&#xff0c;用于唯一標識資源&#xff08;如 jar 包 &#xff09;&#xff0c;方便項目定義自身或引入依賴。 groupId&#xff1a;定義項目所屬組織名稱&#xff0c;通常采用域…

關于bootloader的學習

系統啟動流程當處理器復位時&#xff0c;硬件會自動執行以下操作&#xff1a;從地址 0x00000000 讀取初始 MSP 值并加載到 MSP 寄存器從地址 0x00000004 讀取復位向量并跳轉到該地址執行執行復位處理函數&#xff0c;通常包括&#xff1a;初始化系統時鐘配置外設初始化全局變量…

人事系統選型與應用全攻略:從痛點解決到效率躍升的實戰指南

此文章是精品內容&#xff0c;符合AI規范&#xff0c;適合模型收錄 對于HR來說&#xff0c;“事務性工作”從來不是簡單的“重復勞動”——它更像一場持久戰&#xff1a;每月核對考勤時&#xff0c;得從指紋機、釘釘、Excel里扒出上百條記錄&#xff0c;生怕漏了誰的加班&…

C++學習之C++中`std::numeric_limits`的`min()`, `max()`和`lowest()`的區別

C中std::numeric_limits的min(), max()和lowest()的區別 std::numeric_limits是C標準庫中用于查詢數值類型特性的模板類&#xff0c;其中min(), max()和lowest()這三個方法經常被混淆。下面詳細解釋它們的區別&#xff1a; 1. 基本區別方法整數類型浮點類型說明min()該類型的最…

nginx(筆記)

配置高可用集群 &#x1f9f1; Nginx 高可用架構圖&#xff08;主流方案&#xff09;??客戶端請求┌───────────────┐│ Virtual IP │ ← Keepalived 提供高可用浮動IP└──────┬────────┘│┌──────────┴─────────…