什么是模板

模板分为类模板和函数模板,关键字为template,基本的声明形式如下:

template<class T>;
//也可以写成这样
template<typename T>

class 和 typename 在声明模板参数时的用法是相似的,一般情况下可以互换

但在成员模板内部访问嵌套类型时,需要使用 typename。

下面举一个例子加以理解:

template <class T>
class MyClass {
public:
    typename T::SomeType member; 
    // 使用typename来指明T::SomeType是一个类型
};

模板的原理

模板的原理就是通过所传递的参数类型进行实例化,推导出参数的具体类型,实例化分为显式实例化和隐式实例化。

隐式实例化

当我们不指定参数的类型时,编译器会根据所传递的参数类型进行隐式的实例化,并推导出参数的具体类型。

#include <iostream>

template<class T>
void swap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
int main(void) {
	int a = 10;
	int b = 20;
	swap(a, b);
	std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
	swap(a, b);
	std::cout << "a = " << a << ' ' << "b = " << b << std::endl;

	return 0;
}

这里我们并没有显式的指定参数的类型,所以编译器根据传递的参数自动进行了隐式实例化,并推导出了参数的具体类型。

然而,当我们传递的参数类型不统一时,编译器就无法进行隐式实例化,会导致程序报错。

#include <iostream>

template<class T>
void swap(T& a, T& b) {
	T temp = a;
	a = b;
	b = temp;
}
int main(void) {
	int a = 10;
	double b = 20.1;
	swap(a, b); //double和int类型不统一,隐式实例化失败
	std::cout << "a = " << a << ' ' << "b = " << b << std::endl;
	swap(a, b);
	std::cout << "a = " << a << ' ' << "b = " << b << std::endl;

	return 0;
}

显式实例化

所以,针对以上的情况,我们可以显式的声明参数类型,告诉编译器进行显式的实例化,从而无需对参数类型进行推导。

#include <iostream>

template<class T>
T Add(const T& a, const T& b) {
	return a + b;
}
int main(void) {
	int a = 10;
	double b = 10.2;
	std::cout << Add<int>(a, b) << std::endl; 
  //对Add函数进行了显式的实例化,将参数类型显式的定义为int
	//此时结果为20
	return 0;
}

模板有两种类型,分别是函数模板和类模板,下面分别加以介绍:

函数模板

函数模板较为简单,首先声明模板关键字template,随后实现函数的定义,当我们传入实参时,模板会根据参数类型进行实例化,从而实现模板函数功能。

template<class T>
void swap(T& a, T& b) { //引用传参
	T temp = a;
	a = b;
	b = temp;
}


类模板

类似于函数模板,只不过这里时声明成员参数为模板类型,

下面我将实现一个简单的vector容器加以说明:

这里的vector是一个动态容器,所以要对容器进行动态扩容

#include <iostream>

template<class T>
class vector {
public:
	vector(){ //构造函数初始化
		arr = nullptr;
		_size = 0;
		_capacity = 0;
	}
	~vector() { //析构函数,对容器进行释放操作
		delete []arr;
		arr = nullptr;
	}

	void push_back(const T& target) {
		if (_size == _capacity) { 
      // 判断,并进行扩容
			size_t n_capacity = _capacity == 0 ? 2 : _capacity * 2;
			T* temp = new T[n_capacity]; //在堆上重新分配内存
			if (arr) {
				memcpy(temp, arr, sizeof(T) * _size); //拷贝原数据
				delete[]arr;
        arr = nullptr; 
        //可以省略,下面的代码重置了arr的指向,所以不会出现悬空指针
			}
			arr = temp;
			_capacity = n_capacity;
		}
		arr[_size++] = target;
	}
	T operator[](size_t i) {
		return arr[i];
	}
private:
	T* arr;
	size_t _size;
	size_t _capacity;
};
int main(void) {
	vector<int> vec;
	vec.push_back(1);
	std::cout << vec[0] << std::endl;
	return 0;
}