前言
本次會介紹一下非類型模板參數、模板的特化(特例化)和模板的可變參數,不是最開始學的模板
一、非類型模板參數
字面意思,比如:
template<size_t N = 10>
或者
template<class T,size_t N = 10>
比如:靜態棧就可以用到,并且由于變長數組的原因,這樣可以更好的使用
template<typename T,size_t N = 10>
class Stack {
private:T a[N];int _top;
};
int main()
{Stack<int> s1;Stack<double, 20> s2;return 0;
}
二、模板的特化
模板的特化是C++98中提出的,他處理一種什么情況呢?就是比如說我想對這個模板是int的類型是進行不一樣的操作,其余都一樣,就用上了模板的特化,
1.非類模板的特化
template<typename T>
void func(T a)
{cout << "void func(T a)" << endl;
}
template<>
void func<int>(int a)
{cout << "void func<int>(int a)" << endl;
}
int main()
{func(double());func(int());return 0;
}
通過這個來看要求:需要有一個主模板,不然會報錯,<>中不寫內容,函數名后加<>,里面寫類型,形參與主模板的對應。
如果還有重載的int優先走哪個?當然是重載的然后是模板的
2.類模板的特化
規則與上面類似,但是現在是類名后加<>,里面放類型
template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<>
class A<double> {
public:double a;A() {cout << "class A<double> " << endl;}
};int main()
{A<int> a;A<double> b;return 0;
}
三、全、偏、部分特化
類模板的特化里面呢又搞了幾個東西,偏特化,全特化,部分特化
我這里就用大白話講了,讓大家能夠理解
全特化就是所有的類型全部具體,偏就是全不具體,部分就是一部分具體,上代碼理解一下
全特化:(非類模板只支持全特化,想寫其他的可以去玩重載)
template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<>
class A<double> {
public:A() {cout << "class A<double> " << endl;}
};
template<>
class A<char> {
public:A() {cout << "class A<char> " << endl;}
};
int main()
{A<int> a;A<double> b;A<char>c;return 0;
}
偏特化:比如我就想對所有的指針類型進行特殊處理,所有的指針類型也寫不過來,所以這里用上了偏特化
template<typename T>
class A {
public:A() {cout << "class A " << endl;}
};
template<typename T>
class A<T*> {
public:A() {cout << "class A<T*> " << endl;}
};
template<typename T>
class A<T(*)(int,int)> {
public:A() {cout << "class A < T(*)(int, int)>" << endl;}
};
int main()
{A<int> a;A<double*>b;A<int*>c;A<void(*)(int, int)> d;A<int(*)(int, int)> e;return 0;
}
部分特化,參考全特化和偏特化,就是一部分顯示出來,一部分不顯示
template<typename T,typename G>
class A {
public:A() {cout << "class A" << endl;}
};
template<typename T>
class A<T*,int> {
public:A() {cout << "class A<T*,int> " << endl;}
};
int main()
{A<int, int> a;A<int*, int> b;A<double*, int> c;return 0;
}
沒啥意思,感覺用處也不大
四、模板的可變參數
這里就是C++11搞出來的了
最開始學的能夠接受任意個參數的函數也是C語言用到最多的就是printf和scanf,注意:這里的實現用的并不是模板的可變參數,因為那個時候還沒有C++11(doge,他們是通過宏來實現的奧。
C++里用這個的例子
tuple是元組奧,可能你們沒見過,后面在STL里面會講,也是C++11搞出來的東西
emplace_back也是后面要提到的,先留個底
一個可變參數模板就是一個可接受可變數目參數的模板函數或者模板類,可變數目的參數稱為參數包,存在兩種參數包:模板參數包,表示零個或者多個模板參數,函數參數包,表示零個或者多個參數
先看寫法
template<typename T,typename... Args>
void foo(const T& t,const Args&... rest)
//Args是一個模板參數包,rest是一個函數參數包,均表示0個或者多個函數參數
編譯器從函數的實參推斷模板參數類型,對于一個可變參數模板,編譯器還會推斷包中參數的數目
當我們要知道包里面有多少元素時,可以使用sizeof…運算符,類似sizeof,也返回一個常量表達式
template<class T,class...Args>
void print(T value,Args...args)
{cout << sizeof...(args) << endl;
}
int main() {print(1, 2,4,4);print(1);print(1, 2, 3, 4,"xxxxxxxx");return 0;
}
五、編寫可變參數函數模板
可變參數函數通常是遞歸的,第一步調用處理包中的第一個實參,然后用剩余實參調用自身,所以這里需要兩個參數,比如:
template<class T>
void print(T val)
{cout << val << endl;
}
template<class T,class...Args>
void print(T val, Args...args)
{cout << val << ' ';print(args...);
}
當包里剩一個參數的時候就會調用上面的print(),否則就遞歸自身
這樣是不有一個問題啊,我無法print()什么也不傳
可以再分裝一個函數,然后將第一個函數改成無參,這樣當遞歸到參數包沒有參數就會調用那個函數
void _print()
{cout << endl;
}
template<class T,class...Args>
void _print(T val, Args...args)
{cout << val << ' ';_print(args...);
}
template<class...Args>
void print(Args...args)
{_print(args...);
}
int main()
{print(1, 2);print(1, 2,"xxxxxxxxxx");print(1);print();return 0;
}
Warning:當定義可變參數的print時,非可變參數的版本的聲明必須在作用域中,不然會無限的去遞歸。
六、逗號表達式展開參數包
這種不需要遞歸來實現,是直接在函數體中展開的,
template<class T>
void _print(T val)
{cout << val << ' ';
}
template<class ...Args>
void print(Args... args)
{int arr[] = { (_print(args),0)...};cout << endl;
}
int main()
{print(1, 2);print(1, 2,"xxxxxxxxxx");print(1);return 0;
}
這里了解一下就行,至于為什么用逗號表達式,因為這是int類型的數組,而_print返回值是void,所以扔個0就行,建議記下來也沒啥用
template<class T>
int _print(T val)
{cout << val << ' ';return 1;
}
template<class ...Args>
void print(Args... args)
{//int arr[] = { (_print(args),0)...};int arr[] = { _print(args)... };cout << endl;
}
七、轉發參數包
實際上就是玩forward
八、emplace_back
剛才看了庫里的實現,既然是模板的可變參數,說明就可以這么玩
int main()
{vector<pair<int, int>> v;v.emplace_back(1, 2);v.push_back(1, 2);//error,push_back不可以這么玩return 0;
}
為什么emplace_back支持這么玩?
//別忘了Args&&…args這個不是右值引用,是萬能引用
看第一行,“這個新元素是使用args作為其構造函數的參數就地構造的”相當于直接在容器里面構造,
而push_back是構造 + 移動構造
性能上沒有差很多,因為有了移動構造的緣故,這一步的開銷不是很大
想具體玩差別可以找我上一期的myspace::string,自己調用一下。
總結
這集意義不大,模板的可變參數用的也不多,了解了解就行,emplace_back還是很有用的。明天更新C++11,這個東西巨多,《C++ Primer》第五版這本書上描述了巨多新特性,我會通讀一下然后講一下有用的。模板的可變參數和右值都是C++11里面的,已經繞過了這座大山。