函数模板(参考《C++ Templates 英文版第二版》)
C++模板編程(參考《C++ Templates 英文版第二版》)
Chapter 1 函數模板
1.1 一窺函數模板
template<class T> T max(T a, T b) {return b < a ? a : b; } #include "max1.hpp" #include <iostream> #include <string> #include <format>int main(int argc, char* argv[]) {int i = 42;std::cout << std::format("max(7,i):{}\n", ::max(7, i)); // C++20double f1 = 3.4;double f2 = -9.3;std::cout << std::format("max(f1,f2):{}\n", ::max(f1, f2));std::string s1 = "i am very intelligent";std::string s2 = "i am intelligent";/** 如果不使用::限定max為全局* 則默認使用std::max(),對于在std空間內的類型*/std::cout << std::format("max(s1,s2):{}\n", ::max(s1, s2)); }輸出:
max(7,i):42 max(f1,f2):3.4 max(s1,s2):i am very intelligent模板不會被編譯成一個可以處理任何類型的實例,而是不同的類型使用模板時,模板會生成不同的實例
通過確定的參數代替模板參數的過程叫實例化
1.1.3 兩階段Translation
為了一個不支持相應操作的類型實例化模板會導致錯誤
例如,復數比較大小
因此,模板的"編譯"分兩步:
- 檢查語法錯誤
- 使用不依賴模板參數的名字
- 檢查不依賴于模板參數的靜態斷言
有些編譯器不進行完全的第一階段檢查,你有可能觀察不到上述現象
1.2 函數參數推斷(Template Argument Deduction)
當我們調用函數模板時,模板參數將由我們傳入的參數確定
但是,T 可能只是參數的"一部分",例如,我們我們使用常量引用聲明max()
template<typename T> T amx(T const& a,T const& b) {return a < b ? b : a; }在類型推斷中的類型轉換
在類型推斷中類型轉換是有限的
- 當使用引用傳參時,參數類型必須完全匹配,編譯器不會進行任何微小的轉換
- 當使用值傳參時,支持退化的轉換,const voaltile限定符會被忽略,引用轉換為引用類型,數組,函數將會被轉換成指針類型,兩個參數退化(std::decayed)1后的類型必須匹配
有三個方法解決這個問題:
強制轉換參數使他們都匹配
std::cout << std::format("max1(1,4.4):{}", ::max(static_cast<double>(1), 4.4));顯示指定參數類型
std::cout << std::format("max1(1,4.4):{}", ::max<double>(1, 4.4));默認參數的類型推斷
template<typename T> void f(T = "") { } f(1); // OK:T 推斷為 int,調用 f<int>(1) f(); // 錯誤:不能推斷 T如果想要支持這個例子,你也可以聲明一個默認參數類型
f(); // OK1.3 多種參數模板
函數模板有兩種不同的參數
-
template parameters 模板參數 , 尖括號里面的就是模板參數
-
call parameters 調用參數,就是參數列表里面的
template<typename T> // 模板參數 T max(T a,T b) // a,b就是調用參數你可以使用多個模板參數
template<typename T1,typename T2> T1 max(T1 a,T2 b) {return a < b ? b : a; }這個例子有一些問題,雖然他很有可能正常工作,但是他可能違背程序員的意愿,將返回值強制轉換成T1,例如 double->int
有三種方式可以避免這個問題:
- 再加一個模板參數作為返回值類型
- 讓編譯器清楚返回值類型
- 把返回值聲明成兩個模板參數的共同類型
我們接下來討論:
1.3.1 模板參數作為返回值
我們可以明確的指定參數類型
template<typename T> T max(T a, T b) {return a < b ? b : a; } ::max<int>(1,2.2);但是很多情況下,參數與返回值類型并沒有關系,我們就可以聲明第三個模板參數作為返回值
template<typename T1, typename T2,typename RT> RT max(T1 a, T2 b) {return a < b ? b : a; }但是模板參數推斷并不會考慮到返回值,所也這么做是不行的,除非你手動指定返回值類型
::max<int,double,double>(1,3.3)顯然,這樣做并沒有什么優勢
1.3.2 推斷返回值
template<typename T1,typename T2> auto max(T1 a,T2 b) {return a < b ? b : a; }auto使得真正返回值必須從函數體中推斷出來
然而,在一些情況下,函數傳入參數是引用類型,你想返回值類型,這時你就需要使用std::decay1去掉修飾符
template<typename T1, typename T2> auto max(T1 a, T2 b) ->typename std::decay<decltype(true?a:b)>::type {return a < b ? b : a; }1.3.3 返回公共類型(Common Type)
上面的例子可以直接這么寫
#include <type_traits>template<typename T1, typename T2> std::common_type_t<T1,T2> max(T1 a, T2 b) {return a < b ? b : a; }
1.4 默認模板參數
你也可以定義默認的模板參數
我們可以使用之前的模板參數
template<typename T1, typename T2,typename RT = std::decay_t<decltype(true?T1():T2())>> RT max(T1 a, T2 b) {return a < b ? b : a; }std::decay_t確保不會返回引用
我們可以使用std::common_type_t<>與std::decay相同的效果
template<typename T1, typename T2, typename RT =std::common_type_t<T1,T2>> RT max(T1 a, T2 b) {return a < b ? b : a; }1.5 函數模板重載
// maximum of two int values: int max (int a, int b) {return b < a ? a : b; }// maximum of two values of any type: template<typename T> T max (T a, T b) {return b < a ? a : b; }int main() {::max(7, 42); // calls the nontemplate for two ints::max(7.0, 42.0); // calls max<double> (by argument deduction)::max('a', 'b'); // calls max<char> (by argument deduction)::max<>(7, 42); // calls max<int> (by argument deduction)::max<double>(7, 42); // calls max<double> (no argument deduction)::max('a', 42.7); // calls the nontemplate for two ints }總之,確保只有一個函數匹配
#include <cstring> #include <string>// maximum of two values of any type: template<typename T> T max (T a, T b) {return b < a ? a : b; }// maximum of two pointers: template<typename T> T* max (T* a, T* b) {return *b < *a ? a : b; }// maximum of two C-strings: char const* max (char const* a, char const* b) {return std::strcmp(b,a) < 0 ? a : b; }int main () {int a = 7;int b = 42;auto m1 = ::max(a,b); // max() for two values of type intstd::string s1 = "hey";std::string s2 = "you";auto m2 = ::max(s1,s2); // max() for two values of type std::stringint* p1 = &b;int* p2 = &a;auto m3 = ::max(p1,p2); // max() for two pointerschar const* x = "hello";char const* y = "world";auto m4 = ::max(x,y); // max() for two C-strings }如果用傳引用實現模板,再用傳值重載 C 字符串版本,不能用三個實參版本計算三個 C 字符串的最大值
#include <cstring>// maximum of two values of any type (call-by-reference) template<typename T> T const& max (T const& a, T const& b) {return b < a ? a : b; }// maximum of two C-strings (call-by-value) char const* max (char const* a, char const* b) {return std::strcmp(b,a) < 0 ? a : b; }// maximum of three values of any type (call-by-reference) template<typename T> T const& max (T const& a, T const& b, T const& c) {return max (max(a,b), c); // error if max(a,b) uses call-by-value }int main () {auto m1 = ::max(7, 42, 68); // OKchar const* s1 = "frederic";char const* s2 = "anica";char const* s3 = "lucas";auto m2 = ::max(s1, s2, s3); // run-time ERROR }錯誤原因是 max(max(a, b), c) 中,max(a, b) 產生了一個臨時對象的引用,這個引用在計算完就馬上失效了
重載版本必須在函數調用前聲明才可見
第一章結束
**我所有的學習筆記都在Github倉庫里:https://github.com/fanxing-6/CPP-learning-notes **
,如果訪問Github有問題也可以訪問Gitee:CPP-learning-notes: C++學習筆記 (gitee.com)
本人能力有限,筆記難免有疏漏,如果有錯誤,歡迎各位關注公眾號與我交流,一定會及時回復
Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type. Formally:If T names the type "array of U" or "reference to array of U", the member typedef type is U*.Otherwise, if T is a function type F or a reference thereto, the member typedef type is std::add_pointer::type.Otherwise, the member typedef type is std::remove_cv<std::remove_reference::type>::type.These conversions model the type conversion applied to all function arguments when passed by value.The behavior of a program that adds specializations for decay is undefined. ?? ??
總結
以上是生活随笔為你收集整理的函数模板(参考《C++ Templates 英文版第二版》)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux软件装错了,Linux安装软件
- 下一篇: 二分法(leetcode分类解题,C++