• 将蓝图转换为特定的类或函数,转换发生在编译时期。

  • 当编译器遇到一个模板定义时,它并不生成代码。只有当实例化出模板的一个特定版本时,编译器才会生成代码。

  • 函数模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename T>	// 也可用 class T,但 typename T更直观
int compare(const T &v1, const T &v2) {
if (v1 < v2)
return -1;
if (v2 < v1)
return 1;
return 0;
}

int main(){
int res_1 = compare(1, 0); // 模板实例,编译器自动推断模板参数类型
vector<int> vec1{ 1,2,3 }, vec2{ 4,5,6 };
int res_2 = compare(vec1, vec2); // 模板实例,编译器自动推断模板参数类型
return 0;
}
  • 函数返回值类型使用模板
1
2
3
4
5
6
7
8
9
10
11
template<typename T>
T foo(T *p) {
T tmp = *p;
return tmp;
}

int main(){
int *p = new int(10);
int res = foo(p); // 模板实例,编译器自动推断模板参数类型
return 0;
}
  • 类模板
    • 与函数模板不同,编译器不能为类模板推断模板参数类型,因此生成实例时必须显式指定模板实参列表。
    • 通过模板生成的各个类实例相互完全独立,没有任何关联。
    • 对于一个实例化了的类模板,其成员只有在使用时才被实例化。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
对于普通类,一般将类的定义和有关函数的声明放在头文件,而将普通函数和类的成员函数的定义放在源文件;
对于模板类,一般将声明和定义都放在头文件。
*/
template <typename T>
class TemplateTest {
public:
TemplateTest();
TemplateTest(initializer_list<T>);
private:
vector<T> data;
};

template <typename T>
TemplateTest<T>::TemplateTest() {}

template <typename T>
TemplateTest<T>::TemplateTest(initializer_list<T> init_list) {
for (auto itor = init_list.begin(); itor != init_list.end(); ++itor)
this->data.push_back(*itor);
}

int main(){
initializer_list<int> init_list{ 1,2,3,4,5 };
TemplateTest<int> temp_1; // 显式指定模板实参列表
TemplateTest<int> temp_2(init_list); // 显式指定模板实参列表
return 0;
}
  • 默认模板实参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 如果一个类模板为其所有模板参数都提供了默认实参,且希望使用这些默认实参,就必须在模板名后跟一对空尖括号。
template <typename T = int>
class TemplateTest {
public:
TemplateTest(T v = 0) : val(v) {}
private:
T val;
};

int main(){
TemplateTest<long double> templateTest_1;
TemplateTest<> templateTest_2; // 空<>表示我们希望使用默认类型int
return 0;
}

简单记录各个例子,不做全方位详细记录

返回值是模板的情况 https://blog.csdn.net/hou09tian/article/details/86592640

(class部分虚函数还有好多问号未整理, vs代码也有不少该整理或该删的)