c++ 模版template
函数模板
|
template <class identifier> function_declaration; template <typename identifier> function_declaration; 注意class与typename意义一样,可以互换 例如: //A.h template<typename T> void swap(T& t1, T& t2); //A.cpp template<typename T> void swap(T& t1, T& t2) { T tmpT; tmpT = t1; t1 = t2; t2 = tmpT; } |
简单类模板示例
类模板声明(.h,.hpp文件) //myTemplate.h template <class T> class Stack {
public: Stack(); ~Stack(); void push(T t); T pop(); bool isEmpty(); private: T *m_pT; int m_maxSize,m_size; }; |
|
类模板定义(cpp文件) //myTemplate.cpp template <class T> Stack<T>::Stack(){m_maxSize = 100; m_size = 0;m_pT = new T[m_maxSize];} template <class T> Stack<T>::~Stack() { delete [] m_pT ;} template <class T> void Stack<T>::push(T t) { m_size++; m_pT[m_size - 1] = t; } template <class T> T Stack<T>::pop() { T t = m_pT[m_size - 1]; m_size--; return t; } template <class T> bool Stack<T>::isEmpty() { return m_size == 0; } |
带有常规参数的(且参数分配默认值)的类模板示例
|
下述类模板栈定义,规定默认最多支持100个元素,使用时可以使用模板参数配置栈最大元素数,如果不配置,则默认最大值为100:
|
模板的实例化与特化
概述 |
模板函数不是真正的函数定义,只是如其名提供一个模板,因此必须要提供实话例的代码方可使用(从编译原理与代码段生成来理解,即编译的二进制代码中必须存在有具体使用类型的模板类或代码,方可运行使用,否则链接不到具体代码,链接失败),实例化分隐式实例化与显式实例化两种。 |
隐式实例化 (implicit instantiation) |
在具体使用位置创建: int main(){
.... swap<int>(a,b); .... } 编译器运行编译到此处时,会自动根据模板应用的具体类型生成相应的实例代码段,很显然,这影响编译效率。这里顺便提一下swap<int>(a,b);中的<int>是可选的,因为编译器可以根据函数参数类型自动进行判断,即如果编译器不能自动判断的时候这是必要的; |
显式实例化 (implicit instantiation) |
隐式实例化影响效率,提高效率则使用显式实例化,显式实例化在编译期间无论代码类型是否使用,均会生成实例,方法如下: 在cpp文件根据需要的类型中定义如下行: template void swap<int>(int &a,int &b); 注意:如果模板头文件做成dll 头文件方式输出,则必须要在dll内部cpp文件进行显示实例化或或隐式实例化,不可将实例化留给dll调用者进行,否则编译不过(避免实例在多个调用者程序中,这也不符合dll的原始精神)
|
特化 (specialization,也译作具体化或专门化) |
|
模板别名(c++11)
template<typename T> using twin = std::pair<T, T>;
template<typename T> using str_int = std::pair<T, int>;
std::cout<<"test template alias:\n"; twin<std::string> twin_str = {"abc", "def"};
std::cout<<"twin_str:\t"<<twin_str.first<<'\t'<<twin_str.second<<std::endl; str_int<std::string> strno = {"abc", 100};
std::cout<<"strno:\t"<<strno.first<<'\t'<<strno.second<<std::endl; std::cout<<"test template alias done.\n"<<std::endl; |
尾置返回类型(c++11)
当时用模板定义一个函数时,有时函数的返回类型是和模板参数相关的,这时可以通过decltype获得返回类型。 template<typename It> auto get_begin(It beg) -> decltype(*beg){ return *beg; } std::cout<<"test tail return type of template:\n"; std::vector<int> numbers = {1, 2, 3, 4, 5};
std::cout<<"get_begin:\t"<<get_begin(numbers.begin())<<std::endl; std::cout<<"test tail return type of template done.\n"; |
不定长模板(c++11)
新标准中,可定义不定长度的模板参数列表。这种形式,一般和递归结合使用。 template<typename T> std::ostream &print_variadic(std::ostream &os, const T &t){
return os<<t<<std::endl; } template<typename T, typename... Args> std::ostream &print_variadic(std::ostream &os, const T &t, const Args&... rest){
os<<t<<"(remain size: "<<sizeof...(Args)<<"), "; return print_variadic(os, rest...); } std::cout<<"test variadic template:\n"; print_variadic(std::cout, 100, "s", 56.76, 101); std::cout<<"test variadic template done.\n"; 第一次为print_variadic传入了4个需要打印的对象,则实例化第二个不定长模板函数,将100赋值给t,并将剩余的3个参数打包成rest。 在内部递归中,将不断的将rest包中的第一个参数拿出来付给t,剩余参数打包进行下一次递归调用。最后只剩一个参数时,两种形式的print_variadic都可以匹配,但是第一种没有模版参数包的版本更加特例化,因此将调用第一种形式的print_variadic,结束递归。 |
默认参数(c++11)
|
c++11中,可以像为函数提供默认参数一样,为模板参数提供默认值。 template<typename T, typename F=std::less<T>> int compare(const T &v1, const T &v2, F f=F()){ if(f(v1, v2)) return -1; if(f(v2, v1)) return 1; return 0; } cout<<"test default template param:\n"; cout<<"compare int 1 2:\t" << compare(1, 2) << endl; cout<<"compare int 2.0 1.0:\t" << compare(2.0, 1.0) << endl; cout<<"test default template param done.\n" << endl; |
模板友元(c++11)
声明一个类的模板参数类型为类的友元。 template<typename T> class Bar {
friend T;
protected: int val = 100; }; class Foo{
public: void print_bar(Bar<Foo> &bar) { cout << "bar:\t" << bar.val << endl; }
}; cout<<"test friend template type:\n"; Bar<Foo> bar; Foo foo; foo.print_bar(bar); cout<<"test friend template type done.\n"<<endl; 由于友元机制,在Foo中可以直接访问到Bar的protected属性val。 |

浙公网安备 33010602011771号