模板(Template)
- 模板(Template)指C++程序设计语言中的函数模板与类模板[1],是一种参数化类型机制,大体对应于java和C#中的泛型,但也有一些功能上的显著差异(C++模板支持后两者没有明确对应的模板模板参数和模板非类型参数,但不支持Java的通配符以及C#的泛型类型约束)。模板是C++的泛型编程中不可缺少的一部分。
- 模板是C++程序员绝佳的武器,特别是结合了多重继承与运算符重载之后。C++的标准函数库供的许多有用的函数大多结合了模板的概念,如STL以及iostream。
- 模板是一种工具。使用它会给程序员编写大规模的软件带来方便。因为使用模板可以使程序员建立具有通用类型的函数库和类库。模板也是C++语言支持参数化多态性的工具。将一段程序所处理的对象类型参数化,就可以使这段程序能够处理某个类型范围内的各种类型的对象,这就是参数化多态性。被参数化的一段程序将会处理一定范围内的若干种不同类型的对象,即对于一定范围内的若干不同类型的对象的某种操作将对应着一个相同结构的实现。而模板这种工具就是用来解决这个问题的。由于C++语言的程序结构主要是由函数和类构成的,因此,模板也具有两种不同的形式:函数模板和类模板。
函数模板与模板函数
函数模板是对一组函数的描述,它不是一个实实在在的函数,编译系统并不产生任何执行代码。
当编译系统在程序中发现有与函数模板中相匹配的函数调用时,便生成一个重载函数,该重载函的函数体与函数模板的函数体相同。该重载函数称为模板函数。
函数模板与模板函数的区别如下:
- (1) 函数模板不是一个函数,而是一组函数的模板,在定义中使用了参数类型。
- (2) 模板函数是一种实实在在的函数定义,它的函数体与某个模板函数的函数体相同。
编译系统遇到模板函数调用时,将生成可执行代码。函数模板是定义重载函数的一种工具。一个函数模板只为一种原型函数生成一个模板函数,不同原型的模板函数是重载的。这样就使得一个函数只需编码一次就能用于某个范围的不同类型的对象上。因此,可以说函数模板是提供一组重载函数的样板。
函数模板
以下以取最大值的函数模板maximum为例。此函数在编译时会自动产生对应参数类型的代码,而不用显式声明
#include <iostream>
template <typename T>
inline const T& maximum(const T& x,const T& y)
{
if(y > x){
return y;
}
else{
return x;
}
}
int main(void)
{
using namespace std;
int a=3,b=7;
float x=3.0,y=7.0;
//Calling template function
std::cout << maximum<int>(a,b) << std::endl; //输出 7
std::cout << maximum(a, b) << std::endl; //自动补充类型声明
std::cout << maximum<double>(x,y) << std::endl; //输出 7
return 0;
}
类模板
以下以将组件指针的操作,封装成类别模板ComPtr为例。
#pragma once
template <typename Ty>
class ComPtr
{
protected:
Ty* m_ptr;
public:
ComPtr()
{
m_ptr = NULL;
}
ComPtr(const ComPtr& rhs)
{
m_ptr = NULL;
SetComPtr(rhs.m_ptr);
}
ComPtr(Ty* p)
{
m_ptr = NULL;
SetComPtr(p);
}
~ComPtr()
{
Release();
}
const ComPtr& operator=(const ComPtr& rhs)
{
SetComPtr(rhs.m_ptr);
return *this;
}
Ty* operator=(Ty* p)
{
SetComPtr(p);
return p;
}
operator Ty* ()
{
return m_ptr;
}
Ty* operator->()
{
return m_ptr;
}
operator Ty** ()
{
Release();
return &m_ptr;
}
operator void** ()
{
Release();
return (void**)&m_ptr;
}
bool IsEmpty()
{
return (m_ptr == NULL);
}
void SetComPtr(Ty* p)
{
Release();
m_ptr = p;
if (m_ptr)
{
m_ptr->AddRef();
}
}
void Release()
{
if (m_ptr)
{
m_ptr->Release();
m_ptr = NULL;
}
}
};
依赖名字与typename关键字
一个模板中的依赖于一个模板参数(template parameter)的名字被称为依赖名字 (dependent name)。当一个依赖名字嵌套在一个类的内部时,称为嵌套依赖名字(nested dependent name)。一个不依赖于任何模板参数的名字,称为非依赖名字(non-dependent name)。[4]
编译器在处理模板定义时,可能并不确定依赖名字表示一个类型,还是嵌套类的成员,还是类的静态成员。C++标准规定:如果解析器在一个模板中遇到一个嵌套依赖名字,它假定那个名字不是一个类型,除非显式用typename关键字前置修饰该名字。[5]
typename关键字有两个用途:
常见的在模板定义中的模板形参列表,表示一个模板参数是类型参数。等同于使用class。
- 使用模板类内定义的嵌套依赖类型名字时,显式指明这个名字是一个类型名。否则,这个名字会被理解为模板类的静态成员名。C++11起,这一用途也可以出现在模板以外,尽管此时typename关键字不是必要的。
在下述情形,对嵌套依赖类型名字不需要前置修饰typename关键字:
- 派生类声明的基类列表中的基类标识符;
- 成员初始化列表中的基类标识符;
- 用class、struct、enum等关键字开始的类型标识符
因它们的上下文已经指出这些标识符就是作为类型的名字。例如:
template <class T> class A: public T::Nested { //基类列表中的T::Nested
public:
A(int x) : T::Nested(x) {}; //成员初始化列表中的T::Nested
struct T::type1 m; //已经有了struct关键字的T::type1
};
class B{
public:
class Nested{
public:
Nested(int x){};
};
typedef struct {int x;} type1;
};
int main()
{
A<B> a(101);
return 0;
}
template关键字
template关键字有两个用途:
- 常见的在模板定义的开始。
- 模板类内部定义了模板成员函数或者嵌套的成员模板类。在模板中,当引用这样的模板成员函数或嵌套的成员模板类时,可以在::(作用域解析)运算符、.(以对象方式访问成员)运算符、->(以指针方式访问成员)运算符之后使用template关键字,随后才是模板成员函数名字或嵌套的成员模板类名字,这使得随后的左尖括号<被解释为模板参数列表的开始,而不是小于号运算符。
C++11起,这一用途也可以出现在模板以外,尽管此时template关键字不是必要的。例如:
class A { public:
template <class U> class B{
public: typedef int INT;
};
template <class V> void foo(){}
};
template <typename T>
int f()
{
typename T::template B<double>::INT i;
i=101;
T a, *p=&a;
a.template foo<char>();
p->template foo<long>();
return 0;
}
int main()
{
f<A>();
A::B<double>::INT i; // 自C++11起,也可写作typename A::template B<double>::INT i;
}
起因
- 使用模板有很多原因,最主要的为了得到通用编程的优点。国际标准化组织(ISO)为C++建立了C++标准库,该标准库功能强大,这证明了模板的重要性。库中涉及算法和容器的部分组成了标准模板库(简称STL)。由于模板的可重用性和可扩展性,你可以利用STL来实现效率很高的代码。
总结
- 模板这东西,相当于容器,就vector模板来说吧,我们可以将它想象为排成一列的桶,桶里面的装的东西就是你需要放的元素,也许是int类型、double类型,自定义的结构、联合、类等元素,也可能还是另外的模板(就是说桶里面又有一个小的桶)。
- 至于模板函数,就容易了,不指定任何类型,因为无法确定需要的类型,所以就相当于空在那里,等用户自己来确定,到底该放什么东西在上面。
http://www.cnblogs.com/liaokang/p/5663227.html#.E5.85.B3.E9.94.AE.E5.AD.97 逸蒙blog对于模板(Template)的总结
MSDN:嵌套的类模板
Template Instantiation. [2014-09-27].https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html#Template-Instantiation