Overview
1.Why Templates? 为什么要引入Template模板?
2.Function Template 函数模板
3.Class Template 类模板
4.Generic Programming 泛型编程
Why Templates? 为什么要引入 Template 模板?
假设我们有一个函数 max(int x, int y), 比较两个 int 类型的 x, y 的值
int max(int x, int y) {
return x > y ? x : y;
}
但是有时候我们需要用 max 函数的功能, 去返回两个 double 类型的 x 和 y 的值
double max(double x, double y) {
return x > y ? x : y;
}
所以我们想要一种通用版本的 max 函数写法, 能够支持参数是任何的类型.
intuitively,
1.模板使得函数 (或者类) 支持多个不同的数据类型
2.不同于一般的定义下所有的类型都需要被指定, 使用 template 时, 我们使用1个 (或者多个) placeholder类型
3.类型在定义模板的时候是未知的, 然后用的时候提供具体的类型
4.一旦 template 被定义, 编译器使用 template 去生成很多需要的重载函数 (如果是模板类的话就相应生成多个重载类), 其中每个重载函数 (或者类) 采用不同的类型
5.字典里面模板这个词的解释:
a template is a model that serves as a pattern for creating similar objects. One type of template that is very easy to understand is that of a stencil.
6.C++ 标准库里面使用了大量的模板, 典型的例子是 standard template library (STL, 标准模板库)
Function Template 函数模板
1.函数模板是一种作用在函数上的通用定义, 函数模板用于生成一个或者多个重载函数, 每个函数具有一组不同的实际类型.
2.当我们创建函数模板的时候, 我们用 placehoder type (又叫做 template parameters 或者informally template types ) 来表示任何参数类型, 表示返回类型, 或者表示希望稍后指定的函数体中使用的类型; 其实可以理解为相比int类型/double类型等各种类型, 新定义了一种特殊的类型, 叫做”模板参数”类型
// 用于比较两个数的大小的函数模板
template <typename T> // 模板参数
T max(T x, T y) {
return (x < y) ? y : x;
}
3.函数模板实例化 (function template instantiation)
当编译器遇到函数
max<int>(1, 2)
的时候, 编译器发现函数定义
max<int>(int, int)
并不存在, 因此编译器要使用我们的函数模板
max<T>
去实例化一个函数, 这个过程叫做函数模板实例化 function template instantiation, 被实例化出来的函数叫做函数实例 function instance; 这种因为函数调用而触发的过程叫做 implicit instantiation
4.当第一次实例化完成后, 后续继续调用不需要同类型的函数实例
#include <iostream>
template <typename T>
T max(T x, T y) {
return (a < b) ? b : a;
}
int main () {
cout << std::max<int>(1, 2) << endl; // 实例化, 并调用函数max<int>(int, int)
cout << std::max<int>(4, 3) << endl; // 调用已经实例化的函数 function<int>(int, int)
cout << std::max<double>(1.1, 2.2) << endl; // 实例化, 调用函数max<double>(double, double)
return 0;
}
4.模板参数推导 template argument deduction
当我们想用 int 类型的 max 函数, 通过指定
std::cout << max<int>(1, 2) << endl; // 指定我们想要调用max<int>
我们让编译器去帮忙推导类型
std::cout << max<>(1, 2) << endl; // the compiler will only consider max<int> template function overloads when determining which overloaded function to call.
std::cout << max(1, 2) << endl; // the compiler will consider both max<int> template function overloads and max non-template function overloads.
#include <iostream>
template <typename T>
T max(T x, T y) {
std::cout << "called max<int>(int, int)\n";
return (x < y) ? y : x;
}
int max(int x, int y) {
std::cout << "called max(int, int)\n";
return (x < y) ? y : x;
}
int main() {
std::cout << max<int>(1, 2) << '\n'; // selects max<int>(int, int)
std::cout << max<>(1, 2) << '\n'; // deduces max<int>(int, int) (non-template functions not considered)
std::cout << max(1, 2) << '\n'; // calls function max(int, int)
return 0;
}
输出结果
called max<int>(int, int)
2
called max<int>(int, int)
2
called max(int, int)
2
5.带有非模板参数的函数模板 function templates with non-template parameters
函数模板可以同时有模板参数类型, 也可以有非模板参数类型, 两个可以混着用
template <typename T>
int func(T x, double y) {
return 5;
}
5.实例化的函数不一定被正确编译
比如string类和int类是没办法完成加法的操作的
#include <iostream>
#include <string>
template <typename T>
T addOne(T x) {
return x + 1;
}
int main() {
std::string hello("Hello, world!");
std::cout << addOne(hello) << '\n';
return 0;
}
6.在多个文件中使用模板 using function templates in multiple files
将所有的模板代码都放在.h文件中, 而不放在.cc文件中; 任何需要访问模板的文件都可以#include 相关的头文件,模板定义将由预处理器复制到源文件中, 然后编译器将能够实例化任何需要的函数
add.h
#ifndef ADD_H
#define ADD_H
template <typename T>
T addOne(T x) {// function template definition
return x + 1;
}
#endif
main.cpp
#include "add.h" // import the function template definition
#include <iostream>
int main() {
std::cout << addOne(1) << '\n';
std::cout << addOne(2.3) << '\n';
return 0;
}
7.带有多个模板类型的函数模板
函数模板中, 我们需要用到不止一种类型, 比如同时需要 doule 和 int, 所以可以定义两个模板类型参数; 这种情况下, 参数返回值的类型就需要格外注意了, 因为用到了不止一种类型; c++14 里面一种比较简单的方法是用 auto 类型做返回类型
#include <iostream>
template <typename T, typename U>
T max(T x, U y) {
return (x < y) ? y : x;
}
int main() {
std::cout << max(2, 3.5) << '\n';
return 0;
}
Class Template 类模板
我们可以定义类的模板, 其实就是函数模板推广到 oop 的
#include <iostream>
#include <vector>
template <class T>
class Stack {
private:
vector<T> elems;
public:
T top() const;
void push(T const&);
void pop();
bool empty() const {
return elems.empty();
};
}
template <class T>
void Stack::push(T const& elem) {
elems.push_back(elem);
}
Generic Programming 泛型编程
1.模板类型有时候叫做泛型类型 (generic types) , 用模板编程又叫做泛型编程 (Generic Programming, GP).
2.虽然 cpp 通常非常关注类型和类型检查, 但是泛型编程让我们能够更关注算法和数据结构, 而不去多关注类型信息.
3.模板的优势: 习惯写函数模板有助于大量减少需要维护的代码量
4.模板的缺点:
(i). 函数模板实例化会扩展成大量的代码, 使得编译会很慢. They can expand into a crazy amount of code, which can lead to code bloat and slow compile times;
(ii). 可能造成很多未知的问题. They tend to produce crazy-looking, borderline unreadable error messages that are much harder to decipher than those of regular functions.
5.整体而言, 使用模板的缺点相比使用模板优势还是很小的;
6.使用模板的基础经验是: 首先创建普通函数, 然后发现需要对不同类型进行重载, 则将他们转化成函数模板
Reference
转载请注明来源, from goldandrabbit.github.io