WHCSRL 技术网

简介C++中的模板

科普小文,简介C++中的模板,不涉及艰深内容。

模板把类型定义为参数, 从而实现了代码的可重用性,也减少了代码膨胀。
模版可以分为两类:函数模版和类模版。

类模板支持全特化和偏特化;
函数模板仅支持全特化(因为函数模板的重载可实现偏特化的功能)。

模板实例化
模板定义本身不参与编译,而是编译器根据用户提供的类型参数生成代码,再进行编译。用户提供不同的类型参数,就会实例化出不同的代码。

当模板、全特化、偏特化都存在的时候,编译器在编译阶段进行匹配,优先选择最特殊的。

类模板的成员函数不能是虚函数
编译器在编译一个类的时候,需要知道虚函数表的大小。如果允许类模板的一个成员函数为虚函数,则可以为此成员函数模板实例化出很多个不同版本的虚函数。
编译器为了确定虚函数表的大小,就需要知道整个项目里一共为该成员函数模板实例化了多少个不同版本的虚函数;这就要查找所有的源代码文件,代价非常高。

类模板

template <类型形式参数,e.g. class T>
class ClassName {
    ... 
};

template <类型形式参数,e.g. class T>
return_type ClassName<类型>::member_function_name(...parameters...) {  
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

类模板的全特化

当所有类型参数为特定类型时,类的定义是怎样的。

template<>        // 全特化的类型参数列表是空的
class ClassName<全特化的类型> 
{
    ...
};

// 在定义中,名字后面出现尖括号的语法,大多只是类模板需要,而函数模板不需要;
// 因为编译器可以根据函数签名推导出此函数用的是什么模板
return_type ClassName<全特化的类型>::member_function_name(...parameters...) {
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

类模板的偏特化

当该模板有多个类型参数时,只限定其中的一部分。

要写偏特化,须先有普通模板的定义。如下:

template <class A, class B>
class ClassName {
    ...
private:
    A a;
    B b;
};

template <class C>
class ClassName<int, C> {
    ...
private:
    int a;
    C b;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

函数模板

template <类型形参> 
return_type function_name (...parameters...) {
    ...
}
  • 1
  • 2
  • 3
  • 4

函数模板只有全特化。若想实现偏特化的效果,可以重载。

举例 - 函数模板不能偏特化及其解决方案

MyTemplate.hpp

#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP

#include <iostream>
using namespace std;

template <class A, class B>
void print_func(A a, B b) {
    cout << "Generalized A B" << endl;
}

template <class A, int b>
void print_func(A a, int b) {
    cout << "Specialized for 2nd parameter as int" << endl;
}

template <class A>
void print_func(A a, int b) {
    cout << "First is A, second is int" << endl;
}

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

test_template.cpp

#include "MyTemplate.hpp"

int main()
{

    print_func(1.0, 2.0);
    print_func(1.0, 2);
    
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

执行结果

Generalized A B
First is A, second is int
  • 1
  • 2

最后,
std 命名空间是一个特殊的命名空间,其中不能添加模板,但可以特化已有模板。

(完)

推荐阅读