??作者簡(jiǎn)介:?? 博主在讀機(jī)器人研究生,目前研一。對(duì)計(jì)算機(jī)后端感興趣,喜歡 c + + , g o , p y t h o n , 目前熟悉 c + + , g o 語言,數(shù)據(jù)庫,網(wǎng)絡(luò)編程,了解分布式等相關(guān)內(nèi)容 \textcolor{orange}{博主在讀機(jī)器人研究生,目前研一。對(duì)計(jì)算機(jī)后端感興趣,喜歡c++,go,python,目前熟悉c++,go語言,數(shù)據(jù)庫,網(wǎng)絡(luò)編程,了解分布式等相關(guān)內(nèi)容} 博主在讀機(jī)器人研究生,目前研一。對(duì)計(jì)算機(jī)后端感興趣,喜歡c++,go,python,目前熟悉c++,go語言,數(shù)據(jù)庫,網(wǎng)絡(luò)編程,了解分布式等相關(guān)內(nèi)容
?? 個(gè)人主頁: \textcolor{gray}{個(gè)人主頁:} 個(gè)人主頁: 小呆鳥_coding
?? 支持 : \textcolor{gray}{支持:} 支持: 如果覺得博主的文章還不錯(cuò)或者您用得到的話,可以免費(fèi)的關(guān)注一下博主,如果三連收藏支持就更好啦 \textcolor{green}{如果覺得博主的文章還不錯(cuò)或者您用得到的話,可以免費(fèi)的關(guān)注一下博主,如果三連收藏支持就更好啦} 如果覺得博主的文章還不錯(cuò)或者您用得到的話,可以免費(fèi)的關(guān)注一下博主,如果三連收藏支持就更好啦?? 就是給予我最大的支持! \textcolor{green}{就是給予我最大的支持!} 就是給予我最大的支持!??
??本文摘要??
本專欄主要是對(duì)c++ primer這本圣經(jīng)的總結(jié),以及每章的相關(guān)筆記。目前正在復(fù)習(xí)這本書。同時(shí)希望能夠幫助大家一起,學(xué)完這本書。 本文主要講解第16章 模板與泛型編程 c++ primer 第五版 系列文章:可面試可復(fù)習(xí)
第2章 變量和基本類型
第3章 字符串、向量和數(shù)組
第4章 表達(dá)式
第5章 語句
第6章 函數(shù)
第8章 IO庫
第9章 順序容器
第10章 泛型算法
第11章 關(guān)聯(lián)容器
第12章 動(dòng)態(tài)內(nèi)存
第13章 拷貝控制
第 14章 重載運(yùn)算符
第15章 面向?qū)ο蟪绦蛟O(shè)計(jì)
第 16章 模板與泛型編程
??模板與泛型編程
-
面向?qū)ο缶幊?/code>和
泛型編程
都能處理在編寫程序時(shí)不知道類型的情況- OOP 能處理類型在程序運(yùn)行之前都未知的情況。
- 泛型編程
在編譯時(shí)
就能知道類型了。容器、迭代器、泛型算法都是泛型編程的例子。模板是泛型編程的基礎(chǔ)
,一個(gè)模板就是一個(gè)創(chuàng)建類或函數(shù)的藍(lán)圖。
??16.1 定義模板
??16.1.1 函數(shù)模板
-
模板定義以關(guān)鍵字 template 開始,后跟一個(gè)用 <> 包圍,用逗號(hào)分隔的模板參數(shù)列表。
-
在模板定義中,模板參數(shù)列表不能為空
-
模板參數(shù)列表類似函數(shù)列表,需要在類或函數(shù)定義中用到的類型和值。使用模板時(shí),需要指定模板實(shí)參,將其綁定到模板參數(shù)上。
'普通函數(shù)'
int compare(const string &v1, const string &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
'模板'
template <typename T>
bool compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
實(shí)例化函數(shù)模板
- 調(diào)用函數(shù)模板時(shí),
編譯器用函數(shù)實(shí)參來推斷模板實(shí)參,然后實(shí)例化出一個(gè)特定版本的函數(shù)。
//實(shí)例化出int compare(const int &, const int &)
cout << compare(1, 0) << endl; //T為int
//實(shí)例化int compare(const vector<int> &, const vector<int> &)
vector<int> vec1{1, 2, 3}, vec{4, 5, 6};
模板類型參數(shù)
- 模板類型參數(shù)當(dāng)做內(nèi)置類型或類類型一樣使用
類型參數(shù)可以用來指定返回類型或函數(shù)的參數(shù)類型
模板參數(shù)前必須使用關(guān)鍵字 class 或 typename,兩個(gè)含義相同。
'正確'
template <typename T, class U> calc(const T&, const U&);
'錯(cuò)誤'
template <typename T, U> calc(const T&, const U&);
//T參數(shù)返回類型, T* p參數(shù)類型
template <typename T> T foo(T* p)
{
T tmp = *p
return *p;
}
'實(shí)例'
int a = 0;
int foo(int *p)
{
int tmp = *p;
return tmp;
}
cout << foo(& a) << endl;
非類型模板參數(shù)
- 可以在模板中定義非類型參數(shù)。一個(gè)非類型參數(shù)表示一個(gè)值而非一個(gè)類型。
- 通過一個(gè)特定的類型名
(如int, double等)而非關(guān)鍵字class或typename來指定非類型參數(shù)
當(dāng)一個(gè)模板實(shí)例化時(shí),非類型參數(shù)被用戶提供的值所代替,這些值必須是常量表達(dá)式,以允許編譯器在編譯時(shí)實(shí)例化模板。
- 非類型參數(shù)可以是一個(gè)
整型或指針或引用
。綁定到指針或引用非類型參數(shù)的實(shí)參必須具有靜態(tài)的生存期。 非類型模板參數(shù)的模板實(shí)參必須是常量表達(dá)式(例如指定數(shù)組大?。?/code>
template <unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
return strcmp(p1, p2);
}//定義了一個(gè)函數(shù)模板。
compare("hi", "mom");//實(shí)例化
'實(shí)例化的版本'
int compare(const char (&p1)[3], const char (&p2)[M]);
//編譯器會(huì)在一個(gè)字符串字面值常量的末尾插入一個(gè)空字符作為終結(jié)符
-
注意
一般對(duì)于非類型模板參數(shù),限制const此時(shí)可以傳遞const和非const
inline 和 constexpr 的函數(shù)模板
- 函數(shù)模板可以聲明為 inline 或 constexpr 的。
與普通函數(shù)不同,inline和constexpr說明符放在模板參數(shù)列表之后,返回類型之前
template<typename T> inline T main(const T&, const T&);
編寫類型無關(guān)的代碼
-
編寫泛型代碼有兩個(gè)重要原則:
-
模板中的函數(shù)參數(shù)應(yīng)該是 const 的引用
。引用保證了函數(shù)可以用于不能拷貝的類型,如unique_ptr, IO 類型。 函數(shù)體中的條件判斷僅使用 < 比較運(yùn)算。
-
//缺點(diǎn):對(duì)類型要求比較高,如果調(diào)用它比較倆個(gè)指針,且指針未指向相同的數(shù)組,則會(huì)出錯(cuò)
if (v1 < v2) return -1;
if (v1 > v1) return 1;
return ;
//傳遞指針也正確
template <typename T>
int compare(const T &v1, const T &v2)
{
if (less<T>()(v1, v2)) return -1;
if (less<T>()(v2, v1)) return 1;
return 0;
}
- 注意:
- 模板程序應(yīng)該盡量減少對(duì)實(shí)參類型要求
模板編譯
-
編譯器遇到模板定義時(shí)不生成代碼,當(dāng)實(shí)例化出模板的一個(gè)特定版本時(shí)才生成代碼
。這會(huì)影響錯(cuò)誤何時(shí)被檢測(cè)到。 - 定義類時(shí),普通的成員函數(shù)一般放在源文件中。
但是模板不同,模板的頭文件通常既包括聲明也包括定義
。因?yàn)榫幾g器需要知道函數(shù)模板或類模板成員函數(shù)的完整定義才能進(jìn)行實(shí)例化。
大多數(shù)編譯錯(cuò)誤在實(shí)例化期間報(bào)告
- 第一階段:編譯模板本身時(shí)。只能檢測(cè)到語法錯(cuò)誤。
- 第二階段:編譯器遇到模板使用時(shí)。只能檢測(cè)模板實(shí)參是否與形參相匹配。
- 第三階段:模板實(shí)例化時(shí)。這時(shí)才能發(fā)現(xiàn)類型相關(guān)的錯(cuò)誤。
??16.1.2 類模板
- 類模板與函數(shù)模板不同,
編譯器不能為類模板推斷模板參數(shù)類型
。通過在模板名后的尖括號(hào)中提供額外信息,來代替模板參數(shù)的模板實(shí)參列表
定義類模板
template <typename T> class Blob {}
實(shí)例化類模板
- 當(dāng)使用一個(gè)類模板時(shí),必須提供額外的信息,也就是
顯示模板實(shí)參列表
,他們被綁定到模板參數(shù)
Blob<int> ia; //使用類模板
template <> class Blob<int> {}; // 當(dāng)向上一行那樣使用類模板時(shí),編譯器實(shí)例化出的類就是這樣的。
- 編譯器從Blob模板實(shí)例化出一個(gè)類時(shí),他會(huì)重寫B(tài)lob類,將模板參數(shù)T的每個(gè)實(shí)例,替換為給定的模板實(shí)參,上面就全部替換成了
int類型
注意
- 一個(gè)類模板的每一個(gè)實(shí)例都能形成一個(gè)獨(dú)立的類。例如
Blob<string>與Blob<double>
類型沒有關(guān)聯(lián),也不會(huì)有特殊的訪問權(quán)限,這完全就是倆個(gè)獨(dú)立的Blob類
//定義的實(shí)例化出倆個(gè)不同的Blob類型
Blob<string> names;
Blob<double> prices;
類模板的成員函數(shù)
可以在類模板內(nèi)部,也可以在外部定義成員函數(shù)。定義在類模板內(nèi)部的函數(shù)隱式聲明為內(nèi)聯(lián)函數(shù)。
定義在類模板之外的成員函數(shù)必須以關(guān)鍵字 template 開始,后接類模板參數(shù)列表。
'格式'
template <typename T>
ret_type Blob<T>::member_name(parm-list)
template <typename T> void Blob<T>::check(){}
templte <typename T> T& Blob<T>::operator[]{}
//Blob<T>::在外部定義成員函數(shù)是,必須說明成員屬于哪一個(gè)類
//模板實(shí)參與模板形參必須相同
類模板成員函數(shù)的實(shí)例化
默認(rèn)情況下,一個(gè)類模板的成員函數(shù)只有當(dāng)用到它時(shí)才進(jìn)行實(shí)例化。
- 如果一個(gè)成員函數(shù)沒有被使用,則它不會(huì)實(shí)例化。
在類代碼內(nèi)簡(jiǎn)化模板類名的使用
使用一個(gè)類模板類型時(shí)必須提供模板實(shí)參,在一個(gè)類模板的作用域內(nèi),可以直接使用模板而不敗指定模板實(shí)參
在類模板外使用類模板名必須提供模板實(shí)參。
template <typename T> class BlobPtr{
BlobPtr<T>& operator++();
BlobPtr<T>& operator--();
};
'在類的作用域(內(nèi)部)可以省略'
template <typename T> class BlobPtr{
BlobPtr& operator++();
BlobPtr& operator--();
};
在類模板外使用類模板名
- 當(dāng)在類模板定義其成員時(shí),必須記住,
我們并不在類的作用域中,直到遇到類名才表示進(jìn)入類的作用域
類模板和友元
如果一個(gè)類模板包含一個(gè)非模板友元,則該友元可以訪問該模板的所有實(shí)例。
如果友元也是模板,類可以授權(quán)給所有友元模板實(shí)例,也可以只授權(quán)給特定實(shí)例。
一對(duì)一友好關(guān)系
//每個(gè)Blob實(shí)例將訪問權(quán)限授予用相同類型實(shí)例化的BlobPtr和相等運(yùn)算符
template <typename T> class Blob{
'友元的T和上面的T類型必須一致'
friend class BlobPtr<T>; // 每個(gè) Blob 實(shí)例將訪問權(quán)限授予了同類型實(shí)例化的 BlobPtr。
friend bool operator==<T> (const Blob<T>&,const Blob<T>&); // 將訪問權(quán)限授予同類型實(shí)例化的 ==。
}
'友元的聲明用Blob的模板形參作為他們自己的模板實(shí)參'
'因此友好關(guān)系被限制在相同類型實(shí)例化的Blob和BlobPtr之間'
Blob<char> ca; // BlobPtr<char>和operator==<char> 都是本對(duì)象的友元
Blob<int> ia; // BlobPtr<int>和operator==<int> 都是本對(duì)象的友元
'注意:'
BlobPtr<char> 的成員可以訪問ca的非public部分,但是ca對(duì)ia或其他Blob的任何實(shí)例沒有特殊訪問權(quán)限
通用和特定的模板友好關(guān)系
- 為了讓所有實(shí)例都成為友元,友元聲明中必須使用與類模板不同的模板參數(shù)。
template <typename T> class Blob{
template <typename X> friend class Pal; // Pal 的所有實(shí)例都是 Blob 的每個(gè)實(shí)例的友元。
}
令模板自己的類型參數(shù)成為友元
template <typename T> class Blob{
friend T; // 將訪問權(quán)限授予用來實(shí)例化 Blob 的類型
}
模板類型別名
可以定義一個(gè) typedef 來引用實(shí)例化的類,但不能引用模板
typedef Blob<string> StrBlob;//正確
可以用 using 為類模板定義類型別名
template <typename T> using twins = pair<T, T>; // 為 pair<T, T> 定義了一個(gè)類型別名 twins
twins<string> authors; // authors 是一個(gè) pair<string,string>。
'可以固定一個(gè)或多個(gè)模板參數(shù),上面只是固定一個(gè)'
template <typename T> using partNo = pair<T, unsigned>;
partNo<string>book; //pair<string, unsigned>
定義模板類型別名時(shí),可以固定其中的部分模板參數(shù)
template <typename T> using twins = pair<T, unsigned>; // 為 pair<T, unsigned> 定義了一個(gè)類型別名 twins
twins<string> authors; // authors 是一個(gè) pair<string, unsigned>。
類模板的 static 成員
- 如果類模板定義了 static 成員,那么模板的每個(gè)實(shí)例都有自己獨(dú)有的 static 成員實(shí)例。
static 數(shù)據(jù)成員定義時(shí)也要定義為模板
template <typename T> int Blob<T>::num = 0; // 在類模板外定義 static 數(shù)據(jù)成員的方式。
- 可以通過域訪問符::訪問里面靜態(tài)成員,使用時(shí)才會(huì)被實(shí)例化,也可以通過對(duì)象訪問,
- 但是不能通過模板訪問,因?yàn)槌蓡T必須有個(gè)具體的類才行。
??16.1.3 模板參數(shù)
- 一個(gè)模板參數(shù)的名字沒有實(shí)際意義,既可以寫T,也可以寫其他的名字
模板參數(shù)與作用域
- 模板參數(shù)的可用范圍是其聲明之后,至模板聲明或定義結(jié)束之前。
模板參數(shù)會(huì)隱藏外層作用域中的相同名字,但是注意在模板內(nèi)不能重用模板參數(shù)名。
一個(gè)模板參數(shù)名在一個(gè)特定模板參數(shù)列表中只能出現(xiàn)一次
typedef double A;
template <typename A, typename B> void f(A a, B b)
{
A tmp = a; //tmp的類型為模板參數(shù)A的類型,而非double
double B; //錯(cuò)誤:重聲明模板參數(shù)B
}
模板聲明
模板聲明必須包含模板參數(shù),聲明中的模板參數(shù)的名字不必與定義中相同(與函數(shù)形參類似)。
'3個(gè)calc都指向相同的函數(shù)模板'
template <typename T> T calc (const T &, const T&); //聲明
template <typename U> U calc (const U&, const T&) //聲明
template <typename Type> Type calc(const Type& a, const Type& b){....} //定義
- 一個(gè)文件所需要的所有模板的聲明通常都一起放置在文件的開始位置。
使用類的類型成員
- 默認(rèn)情況下,C++ 假定通過作用域運(yùn)算符訪問的名字不是類型(比如可能是靜態(tài)成員)。
如果希望使用一個(gè)模板類型參數(shù)的類型成員,必須使用關(guān)鍵字 typename 顯式地告訴編譯器該名字是一個(gè)類型,而不能使用class
template <typename T>
typename T::value_type top (const T&c)
{
....
}
默認(rèn)模板實(shí)參
可以為函數(shù)模板和類模板的模板參數(shù)提供默認(rèn)模板實(shí)參,就像可以為函數(shù)參數(shù)提供默認(rèn)實(shí)參一樣。
//compare有一個(gè)默認(rèn)模板實(shí)參less<>和一個(gè)默認(rèn)函數(shù)實(shí)參F()
template <typename T, typename F = less<T>>
int compare(const T &v1, const T &v2, F f = F())
{......}
模板默認(rèn)實(shí)參與類模板
- 如果一個(gè)類模板為其所有模板參數(shù)都提供了默認(rèn)實(shí)參,且希望使用默認(rèn)實(shí)參,必須在模板名后面跟一個(gè)空尖括號(hào)對(duì):
??16.1.4 成員模板
- 一個(gè)類(不管是普通類還是類模板)都可以包含本省是模板的成員函數(shù)。
- 成員模板(member template):本身是模板的函數(shù)成員。
- 普通(非模板)類的成員模板。
- 類模板的成員模板。
普通(非模板)類的成員模板
注意:成員模板不能是虛函數(shù)
類模板的成員模板
- 可以對(duì)類模板,定義其成員模板,此時(shí)類模板與成員模板都是獨(dú)立的模板
- 與類模板的
普通函數(shù)成員
不同的是, 成員模板是函模板。當(dāng)在類模板外定義一個(gè)成員模板時(shí),必須同時(shí)為類模板和成員模板提供模板參數(shù)列表。
template<typename T> class Blob{
template<typename it> Blob(lt b, lt e);
....
};
template<typename T> //類的類型參數(shù)
template<typename ll> //構(gòu)造函數(shù)的類型參數(shù)
Blob<T>::Blob(lt b, lt e);
data(std:make_shared<std::vector<T>>(b, e)){}
實(shí)例化與成員模板
- 實(shí)例化一個(gè)類模板的成員模板,必須同時(shí)提供類和函數(shù)模板的實(shí)參
- 倆步走:
- 首先根據(jù)對(duì)象的類型推斷出類模板的參數(shù)的實(shí)參
- 然后根據(jù)傳遞給成員模板的函數(shù)實(shí)參來推斷它的模板實(shí)參
第一步:定義a1時(shí),顯示的指出編譯器應(yīng)該實(shí)例化一個(gè)int版本Blob
第二步:構(gòu)造函數(shù)自己的類型參數(shù)通過begin(a1)和end(a1)類型推斷
??16.1.5 控制實(shí)例化
-
產(chǎn)生控制實(shí)例化原因:
- 當(dāng)模板被使用時(shí)才會(huì)進(jìn)行實(shí)例化,而每個(gè)模板的源碼都單獨(dú)的放在一個(gè)文件中,當(dāng)進(jìn)行單獨(dú)編譯是,就會(huì)出現(xiàn)重復(fù)(在多個(gè)文件中實(shí)例化相同模板的額外開銷可能非常嚴(yán)重。)
-
顯示實(shí)例化:
-
extern template declaration; // 實(shí)例化聲明
-
template declaration; // 實(shí)例化定義
extern template class Blob<string>; //聲明
template int compare(const int &, const int &); //定義
??16.1.6 效率與靈活性
??16.2 模板實(shí)參推斷
編譯器通常不是對(duì)實(shí)參進(jìn)行類型轉(zhuǎn)換,他不知道到怎么轉(zhuǎn)換,而是生成一個(gè)新的模板實(shí)例
??16.2.1 類型轉(zhuǎn)換與模板類型參數(shù)
- 通常不會(huì)進(jìn)行類型轉(zhuǎn)換,但是有特例
-
const轉(zhuǎn)換
:可以將一個(gè)非const對(duì)象的引用(指針)傳遞給一個(gè)const的引用(或指針)形參 -
數(shù)組或函數(shù)指針轉(zhuǎn)換
:如果函數(shù)形參不是引用類型,則可以將數(shù)組或函數(shù)轉(zhuǎn)換為相應(yīng)的指針。(數(shù)組實(shí)參轉(zhuǎn)換為一個(gè)指向其首元素的指針,函數(shù)實(shí)參轉(zhuǎn)換為指向一個(gè)該函數(shù)類型的指針)
使用相同 模板參數(shù)類型的函數(shù)形參
- 一個(gè)模板類型參數(shù)可以用作多個(gè)函數(shù)形參的類型。但是只允許有限的幾種類型轉(zhuǎn)換,因此傳遞給這些形參的實(shí)參必須具有相同的類型
- 如果推斷出類型不匹配,只能傳遞倆個(gè)類型參數(shù)
long lng;
compare(lng, 1024); //錯(cuò)誤:不能實(shí)例化compare(lng, 1024);類型不匹配且不能轉(zhuǎn)換
template<typename A, typename B>
int flexibleCompare(const A& v1, const B& v2){....};
long lng;
flexibleCompare(lng, 1024); //正確但是有一個(gè)條件,這里面?zhèn)z個(gè)類型是兼容的可以進(jìn)行比較。
正常類型轉(zhuǎn)換應(yīng)用于普通函數(shù)實(shí)參
- 函數(shù)模板可以用普通類型定義的參數(shù),這種函數(shù)可以進(jìn)行進(jìn)行形參的轉(zhuǎn)換
(對(duì)于模板類型,只允許幾種情況進(jìn)行自動(dòng)轉(zhuǎn)換,intL類型不可以轉(zhuǎn)換,但是對(duì)于普通類型來說,可以進(jìn)行轉(zhuǎn)換)
??16.2.2 函數(shù)模板顯示實(shí)參
- 一般情況編譯器無法推斷模板實(shí)參類型,此時(shí)需要對(duì)他指定顯示模板實(shí)參
指定顯示模板實(shí)參
'只有實(shí)例化后編譯器才能推導(dǎo)出類型'
template <typename T1, typename T2, typename T3> T1 sum(T2, T3);
'顯示模板實(shí)參'
auto val3 = sum<long long>(i, lng); // T1是顯式指定,T2和T3都是從函數(shù)實(shí)參類型推斷而來
-
當(dāng)需要給定T3類型時(shí),T1和T2都要提前給,否則會(huì)出錯(cuò),所以對(duì)于這種糟糕的設(shè)計(jì),三個(gè)類型都要顯示給出
正常類型轉(zhuǎn)換應(yīng)用于顯示指定的實(shí)參
- 對(duì)于普通類型定義的函數(shù)參數(shù)可以進(jìn)行正常的轉(zhuǎn)換,同樣對(duì)于模板類型參數(shù)已經(jīng)
顯示指定了的函數(shù)實(shí)參
,也可以正常的類型轉(zhuǎn)換
??16.2.3 尾置返回類型與類型轉(zhuǎn)換
- 當(dāng)確定返回類型時(shí),可以顯示模板實(shí)參表示模板函數(shù)的返回類型,但是這樣會(huì)增加額外負(fù)擔(dān)。
-
decltype(*lt)``lt
既不是迭代器對(duì)象,也不是指針對(duì)象,只是一個(gè)類型,因此不可以這樣使用 -
decltype(*beg)
,可以這樣使用,但是在編譯器遇到函數(shù)的列表之前,beg都是不存在的 - 可以使用
尾置返回類型
,由于尾置返回出現(xiàn)在參數(shù)列表之后,他可以使用函數(shù)的參數(shù)
??16.2.4 函數(shù)指針和實(shí)參推斷
當(dāng)使用一個(gè)函數(shù)模板初始化一個(gè)函數(shù)指針或?yàn)橐粋€(gè)函數(shù)指針賦值時(shí),編譯器使用指針的類型來推斷模板實(shí)參。
- 當(dāng)參數(shù)是一個(gè)函數(shù)模板實(shí)例的地址時(shí),程序上下文必須滿足,對(duì)于每個(gè)模板參數(shù)能唯一確定其類型或值
??16.2.5 模板實(shí)參推斷和引用
從左值引用函數(shù)參數(shù)推斷類型
從右值引用函數(shù)參數(shù)推斷類型
- 從右值引用推導(dǎo),這樣就可以接收右值
template<typename T> void f3(T&&)
f3(42); //實(shí)參是一個(gè)int類型的右值;模板參數(shù)T是int
引用折疊和右值引用參數(shù)
- 引用折疊只能應(yīng)用于間接創(chuàng)建引用的引用,如類型別名或模板參數(shù)
- 引用的引用是不存在的,但是現(xiàn)在有了,只能進(jìn)行引用折疊
只有倆個(gè)都是右值引用才能折疊成右值引用
- 右值引用既可以接收左值,也可以接收右值
編寫接收右值引用參數(shù)的模板函數(shù)
template<typename T> void f3(T && val)
{
T t = val; //拷貝還是綁定一個(gè)引用
t = fcn(t); //賦值只改變t還是既改變t又改變val;
if(val == t); //若T是引用類型,則一直是true
}
'對(duì)一個(gè)右值調(diào)用f3時(shí),例如字面值常量42,T為int'
'對(duì)一個(gè)左值i調(diào)用f3時(shí),T為int &,此時(shí)t和val綁定在一起','結(jié)果一直為true'
- 在實(shí)際中,右值引用通常用于倆種情況:
模板轉(zhuǎn)發(fā)其實(shí)參
模板被重載
'目前使用右值引用的函數(shù)模板通常使用481頁的方式重載'
template <typename T> void f(T&&); //綁定到非const右值
template <typename T> void f(const T&); //左值和const 右值
??16.2.6 理解std:move
move函數(shù)可以將左值引用變?yōu)橛抑狄茫m然不能直接將一個(gè)右值引用綁定到一個(gè)左值,但是用move獲得綁定到左值上的右值引用)
std::move是如何定義的
std::move是如何工作的
??16.2.7 轉(zhuǎn)發(fā)
- 某些函數(shù)需要將一個(gè)或多個(gè)實(shí)參連同類型不變地轉(zhuǎn)發(fā)給其他函數(shù)。因此需要保持轉(zhuǎn)發(fā)實(shí)參的所有性質(zhì),包括實(shí)參類型是否是
const
的以及實(shí)參是左值還是右值 - 使用一個(gè)名為
forward
的新標(biāo)準(zhǔn)庫設(shè)施來傳遞參數(shù),它能夠保持原始實(shí)參的類型。 - 定義在頭文件
utility
中。 - 必須通過顯式模板實(shí)參來調(diào)用。
-
forward
返回顯式實(shí)參類型的右值引用。即,forward<T>
的返回類型是T&&
。
??16.3 重載與模板
- 函數(shù)模板可以被另一個(gè)模板或一個(gè)普通非模板函數(shù)重載。與普通函數(shù)重載一樣,名字相同的函數(shù)必須具有不同數(shù)量或類型的參數(shù)
編寫重載模板
int main()
{
string s("hi");
'模板一更精確'
cout << debug_rep(s) << endl; //調(diào)用第一個(gè)模板,T類型為string
'模板二更精確'
cout << debug_rep(&s) << endl; //地址可以被封裝成指針,
//debug_rep(cosnt string*&),由第一個(gè)版本的debug_rep實(shí)例化而來,T被綁定到string *
//debug_rep( string*),由第二個(gè)版本的debug_rep實(shí)例化而來,T被綁定到string
'無法確定,但是根據(jù)規(guī)定,選擇最特化版本'
const string *sp = &s;
cout << debug_rep(sp) << endl; //都是精確匹配,但是第二個(gè)更特化
//debug_rep(cosnt string*&),由第一個(gè)版本的debug_rep實(shí)例化而來,T被綁定到string *
//debug_rep(cosnt string*),由第二個(gè)版本的debug_rep實(shí)例化而來,T被綁定到const string
}
非模板和模板重載文章來源:http://www.zghlxwxcb.cn/news/detail-828054.html
對(duì)于一個(gè)調(diào)用,如果一個(gè)非函數(shù)模板與一個(gè)函數(shù)模板提供同樣好的匹配,則選擇非模板版本。
??16.4 可變參數(shù)模板
可變參數(shù)模板就是一個(gè)接受可變數(shù)目參數(shù)的模板函數(shù)或模板類。文章來源地址http://www.zghlxwxcb.cn/news/detail-828054.html
- 可變數(shù)目的參數(shù)被稱為參數(shù)包。
- 模板參數(shù)包:標(biāo)識(shí)另個(gè)或多個(gè)模板參數(shù)。
- 函數(shù)參數(shù)包:標(biāo)識(shí)另個(gè)或者多個(gè)函數(shù)參數(shù)。
- 用一個(gè)省略號(hào)來指出一個(gè)模板參數(shù)或函數(shù)參數(shù),表示一個(gè)包。
-
template <typename T, typename... Args>
,Args
第一個(gè)模板參數(shù)包。 -
void foo(const T &t, const Args& ... rest);
,rest
是一個(gè)函數(shù)參數(shù)包。 -
sizeof...
運(yùn)算符,返回參數(shù)的數(shù)目。
??16.4.1 編寫可變參數(shù)函數(shù)模板
- 可變參數(shù)函數(shù)通常是遞歸的:第一步調(diào)用處理包中的第一個(gè)實(shí)參,然后用剩余實(shí)參調(diào)用自身。
??16.4.2 包擴(kuò)展
- 對(duì)于一個(gè)參數(shù)包,除了獲取它的大小,唯一能做的事情就是擴(kuò)展(expand)。
- 擴(kuò)展一個(gè)包時(shí),還要提供用于每個(gè)擴(kuò)展元素的模式(pattern)。
??16.4.3 轉(zhuǎn)發(fā)參數(shù)包
- 新標(biāo)準(zhǔn)下可以組合使用可變參數(shù)模板和
forward
機(jī)制,實(shí)現(xiàn)將實(shí)參不變地傳遞給其他函數(shù)。
??16.5 模板特例化
- 定義函數(shù)模板特例化:關(guān)鍵字
template
后面跟一個(gè)空尖括號(hào)對(duì)(<>)。 - 特例化的本質(zhì)是實(shí)例化一個(gè)模板,而不是重載它。特例化不影響函數(shù)匹配。
- 模板及其特例化版本應(yīng)該聲明在同一個(gè)頭文件中。所有同名模板的聲明應(yīng)該放在前面,然后是特例化版本。
- 我們可以部分特例化類模板,但不能部分特例化函數(shù)模板。
到了這里,關(guān)于【c++ primer 筆記】第 16章 模板與泛型編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!