相关内容:
泛型编程就是指编写独立于特定类型的代码,c++ STL
就是泛型编程的极致运用。比如vector
,它就是一个泛型容器,它里面可以装n
多种类的元素。在使用的时候,再去指定元素类型:vector<int> vi;
或者vector<float> vf;
模板是泛型编程的根基,没有模板的支持,泛型编程就无从谈起。
函数模板(function template
)
函数模板是一种独立于类型的特殊函数,由函数模板可以产生针对特定类型的函数版本。
1 | template <typename T> |
上面这个compare
函数既可以比较两个int
类型的数,也可以比较两个string
类型的字符串。从这就可以看出模板与泛型编程的强大。如果不运用模板技术,而且我既想比较int
类型,又想比较string
类型,又想比较自定义类型等等,那就要针对这些类型分别编写一个函数,这样既增加了代码量,也不容易维护。
<typename T>
称为模板形参表,T
就是一个模板形参,多个模板形参以逗号隔开,模板形参表不允许为空。
- 1) 函数模板并不是真正的函数,它只是C++编译生成具体函数的一个模子。
- 2) 函数模板本身并不生成函数,实际生成的函数是替换函数模板的那个函数。这种替换是编译期就绑定的。
- 3) 函数模板不是只编译一份满足多重需要,而是为每一种替换它的函数编译一份。
- 4) 函数模板不允许自动类型转换。
- 5) 函数模板不可以设置默认模板实参。比如
template
不可以。 - 6) 函数模板的模板形参不能为空。
函数模板的使用
对于“1
2
3
4
5
6int main()
{
cout << compare(3, 5) << endl;
string s1 = "hello", s2 = "world";
cout << compare(s1, s2) << endl;
}compare(3, 5)
”,编译器将首先推断出函数参数的类型为int
,然后将int
绑定到compare
的模板形参T
,这个过程成为实例化(instantiate
)了函数模板的一个实例。其实就是编译器帮助程序员编写了一个int
版的compare
。模板形参类型
模板形参有两种类型,一种叫做类型形参(type parameter
)。如上面的“typename T
”就是类型形参,因为T
代表一个类型。还有一种模板形参叫做非类型形参(nontype parameter
)。这里面“1
2
3
4
5
6
7
8template <typename T, size_t N>
void array_init(T (&parm)[N])
{
for (size_t i = 0; i != N; ++i)
{
parm[i] = i;
}
}size_t N
”就是非类型形参,因为它并不代表某个类型。当调用array_init
时,编译器从数组实参计算出函数模板的非类型形参的值。如:编译器将函数模板1
2int ia[10];
array_init(ia);array_init
的形参绑定到int[10]
,从而实例化一个array_init
版本如下:array_init(int (&)[10])
,也就是N
的值为10
。模板形参的名字不能在模板内部重用
这段代码会出现编译错误,因为模板形参T,在模板内部不能重用。1
2
3
4
5
6
7template <typename T>
int compare(const T &lhs, const T &rhs)
{
typedef int T; //error
// …
return 0;
}内联(
函数模板也可以声明为inline
)函数模板inline
,格式如下(注意inline
的位置):1
template <typename T> inline int compare(const T &lhs, const T &rhs);
为什么成员函数模板不能是虚函数(
这是因为virtual
)?c++ compiler
在parse
一个类的时候就要确定vtable
的大小,如果允许一个虚函数是模板函数,那么compiler
就需要在parse
这个类之前扫描所有的代码,找出这个模板成员函数的调用(实例化),然后才能确定vtable
的大小,而显然这是不可行的,除非改变当前compiler
的工作机制。函数模板和模板函数是什么?
- 函数模板的重点是模板。表示的是一个模板,专门用来生产函数。
- 模板函数的重点是函数。表示的是由一个模板生成而来的函数。
类模板(
所谓类模板就是说本类可以支持不同类型的对象。如:class template
):类模板的定义与普通类的定义看起来差不多,主要不同就是类型T,使得Queue可以针对不同的类型实例化不同的类。如:1
2
3
4
5
6
7
8
9
10
11
12
13template <typename T>
class Queue
{
public:
Queue();
T &front();
const T &front()const;
void push(const T &);
void pop();
bool empty() const;
private:
// ...
};需要注意的是类模板也仅仅是一个模板,它并不是一个类,所以:1
2Queue<int> qi;
Queue<string> qs;Queue q;
是不会通过编译的,因为Queue
并不是一个类型。而Queue<int>
才是根据类模板Queue
实例化出来的一个具体的类,所以可以定义一个Queue<int>
类型的变量qi
。在模板定义的内部指定类型
1
2
3
4
5
6template <typename Parm, typename U>
Parm fcn(Parm* array, U value)
{
Parm::size_type * p;
// ...
}Parm
是一个类,类既可以有数据成员和函数成员,也可以有类型成员,比如STL
容器类都有一个类型成员:size_type
。而在上面这个函数模板的内部,编译器在遇到“Parm::size_type
”时,不知道它是一个数据,还是一个类型,通常编译器假定这样的名字是一个数据,所以“Parm::size_type * p
”就是两个数据的相乘,这肯定不是我们想要的结果。为了让编译器知道“Parm::size_type
”是一个类型,就要显示的在其前面加上“typename
”,如下:1
2
3
4
5
6template <typename Parm, typename U>
Parm fcn(Parm* array, U value)
{
typename Parm::size_type * p;
// ...
}参考链接:
c++模板与泛型编程(一)模板定义 ——《c++ primer》读书笔记
C++11新特性之泛型编程与模板