函數模板
顯示實例化
區別定義與聲明
T是模板形參 int是模板實參?
inpunt是函數形參 3是函數實參
顯示實例化
模板必須實例化可見 翻譯單元一處定義原則
與內聯函數異同
引入原因:函數模板是為了編譯器兩個階段的處理 內聯函數是為了能在編譯期展開
模板實參的類型推導
推導原則
推導規則示例
1.
- 函數形參是左值引用/指針:
- 忽略表達式類型中的引用
- 將表達式類型與函數形參模式匹配以確定模板實參
2.萬能引用:函數模板的 T&&是萬能引用?
- 如果實參表達式是右值,那么模板形參被推導為去掉引用的基本類型
- 如果實參表達式是左值,那么模板形參被推導為左值引用,觸發引用折疊
引用折疊: 當有兩個引用相互綁定時,它們會被折疊成一個引用。在模板實例化期間,引用折疊規則被應用于模板參數。
3、
- 函數形參不包含引用
- 忽略表達式類型中的引用
- 忽略頂層
const
- 數組、函數轉換成相應的指針類型
無法推導的情況
- 模板實參并非總是能夠推導得到
- 如果模板形參與函數形參無關,則無法推導
- 即使相關,也不一定能進行推導,推導成功也可能存在因歧義而無法使用
即使相關,也不一定能進行推導,推導成功也可能存在因歧義而無法使用
- 在無法推導時,編譯器會選擇使用缺省模板實參
- 可以為任意位置的模板形參指定缺省模板實參——注意與函數缺省實參的區別
但是缺省只能處理無法推導的情況 不能處理能推導但是有歧義的情況
模板缺省實參和函數缺省實參區別
函數缺省實參要從右向左設置。 模板可以不是
自動推導遇到的幾種情況
1.SPFINAE
函數模板中的替換失敗(Substitution Failure Is Not An Error,簡稱 SFINAE)是一種編譯器處理模板實例化失敗的機制。當嘗試實例化一個模板時,如果由于某些原因導致實例化失敗,編譯器并不會報錯,而是會嘗試使用備選的模板或者進行其他處理。
報錯:沒有匹配的函數 、忽視函數
重載函數 匹配失敗就會找其他模板實例化 忽略掉前一個
2.模板與非模板同時匹配,匹配等級相同,此時選擇非模板的版本
下面的代碼調用了非模板的fun函數
注意 是匹配等級相同的情況才會選擇非模板 如果不同?則會選擇更加完美的匹配
下面的代碼會調用模板fun
3.多個模板同時匹配,此時采用偏序關系確定選擇最特殊的版本
float更加特殊 所以匹配第二個fun
如果同樣特殊就會報錯
標準類型轉換模板
尾置返回類型與類型轉換
- 顯式實例化定義:
template void fun(int)
?/?template void fun(int)
- 顯式實例化聲明:
extern template void fun(int)
?/?extern template void fun(int)
- 注意一處定義原則
- 來源:stackoverflower
- 注意實例化過程中的模板形參推導
模板特化
函數模板的(完全)特化:
template<> void f(int)
?/?template<> void f(int)
- 并不引入新的(同名)名稱,只是為某個模板針對特定模板實參提供優化算法
避免使用模板的特化
- 不參與重載解析,會產生反直覺的效果
- 通常可以用重載代替
- 一些不便于重載的情況:無法建立模板形參與函數形參的關聯
1.if constexpr解決
?
2.假函數解決
函數模板不能偏特化
C++20auto定義模板參數
類模板
成員函數只有在調用時才會被實例化
證明:程序無法編譯 但是去掉21行就能編譯
- 類內類模板名稱的簡寫
類模板成員函數類外定義
成員函數模板
類的成員函數模板
類模板的成員函數模板
類內定義
類外定義
友元函數模板
fun是個友元函數模板
類模板的靜態成員
成員對象只有類實例化的時候才會被實例化
static函數只有使用的時候才會實例化
C++11模板參數為友元
類模板的實例化
類模板的顯示實例化
類模板特化
完全特化
偏特化
C++17類模板的實參推導
B x(3)自動推導成B<int>
pair的自動推導
C++17之前的解決方法 函數模板推導
C++20新概念
- 模板的問題:沒有對模板參數引入相應的限制
- 參數是否可以正常工作,通常需要閱讀代碼進行理解
- 編譯報錯友好性較差(
vector<int&>
)
- (C++20)
Concepts
:編譯期謂詞,基于給定的輸入,返回true
或false
- 與
constraints
(require
從句)一起使用時限制模板參數 - 通常置于表示模板形參的尖括號后面進行限制
- 與
此處限制T是int和float
優點:報錯一目了然
Concept
的定義與使用
- 1.包含一個模板參數的
Concept
- 使用
requires
從句 - 直接替換
typename
- 使用
- 2.包含多個模板參數
Concept
- 用做類型
constraint
時,少傳遞一個參數,推導出的類型將作為首個參數
- 用做類型
requires 表達式 (C++20 起) - cppreference.com? ?參考資料
- 簡單表達式:表明可以接收的操作
- 類型表達式:表明是一個有效的類型
requires
從句所引入的限定具有偏序特性,系統會選擇限制最嚴格的版本
如下 C1更嚴格 編譯器選擇匹配C1
- 特化小技巧:在聲明中引入“
A||B
”進行限制,之后分別針對A
與B
引入特化
重載與模板
數值模板參數與模板模板參數
模板可以接受編譯器常量為模板參數
C++17auto value
·
C++20支持浮點數作為模板參數(clang 12不支持)
接受模板作為模板參數
C++17模板的模板考慮缺省實參
clang12支持有限
別名模板
- 為目標本身引入別名
- 為類模板的成員引入別名
- 別名模板不支持特化,但可以基于類模板的特化引入別名,以實現類似特化的功能
- 注意與實參推導的關系
變長模板
變長模板(Variadic Template)
- 變長模板參數與參數包
形參包(parameter pack)是C++中用于處理可變數量參數的一種特性。形參包可以接受任意數量的模板參數,并在模板中進行處理。
形參包的基本語法是使用...
來表示,可以用在函數模板、類模板以及別的模板上下文中。形參包的展開可以通過遞歸、折疊表達式(C++17引入)等方式進行。
接數值
接類型
帶可選名字的函數形參包
完美轉發
右值引用失效
使用萬能引用 T&&情況
還是失效 原因:右值引用是個左值
解決方法:轉發 forward
- (C++11)完美轉發:
std::forward
函數- 通常與萬能引用結合使用
- 同時處理傳入參數是左值或右值的情形
包展開與折疊表達式
消除歧義
internal*p被編譯器解讀成乘法
- 使用
typename
與template
消除歧義- 使用
typename
表示一個依賴名稱是類型而非靜態數據成員
- 使用
使用template
表示一個依賴名稱是模板
T::internal<A 會被編譯器視為小于號比較大小
使用template
表示一個依賴名稱是模板
template
與成員函數模板調用
internal被解讀為依賴obj <被解讀成小于號
解決方法
C++14變量模板
- (C++14)變量模板
template<typename T> T pi = (T)3.1415926;
- 其他形式的變量模板
C++14 引入了變量模板(Variable Templates)的概念,它允許你定義參數化的變量,類似于函數模板允許你定義參數化的函數。變量模板提供了一種通用的方式來定義與類型相關的常量或變量,使得代碼更具有通用性和靈活性。
lambda模板表達式
// 使用 C++20 中的 lambda 模板auto genericLambda = []<typename T>(T x, T y) {return x + y;};