c++模板 --非类型模板参数,模板特化,模板分离编译详解

非类型模板参数

模板参数分为类型形参和非类型形参。

  • 类型形参 出现在函数参数类表中,用来实例化的时候确定该参数的类型(一般是内置类型)
  • 非类型形参 用一个常量作为模板的一个参数, 在类中当做常量使用。

c/cpp不支持数组空间动态定义。于是我们可以使用非类型模板参数来定义数组长度,从而达到我们想要的结果。

注意: 非类型模板参数一般使用的都是int char short等整数,浮点数,字符串等不允许当作模板参数。

template<class T, size_t N = 10>//N一般要求是int short char 
   class array {
   public:
   	//重载[]
   	T& operator[](size_t index) {
   		return _array[index];
   	}
   	const T& operator[](size_t index) const{
   		return _array[index];
   	}
   	//数组长度接口
   	size_t size()const {
   		return _size;
   	}
   private:
   	T _array[N];
   	size_t _size;
   };

模板特化

所谓特化,就是指对函数模板的类型加以限制得到我们想要的结果。
我们都知道模板分为函数模板与类模板 下面就两种模板的特化分别研究。

函数模板

比如下面的比较字符串是否相等的模板函数, 如果按照原来的模板进行比较, 我们比较的就不是字符串是否相等,就达不到我们预期的结果,因此我们对函数模板进一步特化,当传入的是char*类型的时候我们就调用特化的模板函数。但是这种做法却比较冗余,我们可以直接定义一个函数用来处理字符串即可,一次函数模板的特化视情况而定,像下面这种情况就不如重新写一个处理字符串相等的函数来的好。

函数模板特化的步骤

  • 有基础模板函数
  • 关键字后面<>
  • 函数名后面尖括号,尖括号中制定特化类型
  • 函数形参必须和指定的特化类型一样
template<class>
bool IsEqual(T& left, T& right) {
	return left == right;
}
template<>
	bool IsEqual<char*>(char*& left, char*& right) {
		if (strcmp(left, right) > 0)
			return true;

		return false;
	}
	
	bool IsEqual(char* left, char* right) {
		if (strcmp(left, right) > 0)
			return true;

		return false;
	}

类模板

类模板分为全特化与偏特化。

  • 全特化:将模板参数全部指定。
  • 偏特化:对模板函数加以限制,而不直接确定。

	template<class T1, class T2>//normal
	class Data {
	public:
		Data() {
			std::cout << "normal" << std::endl;
		}
	private:
		T1 _d1;
		T2 _d2;
	};

	template<>
	class Data<int,char> {
	public:
		Data() {
			std::cout << "all" << std::endl;
		}
	private:
		int _d1;
		char _d2;
	};

	//偏特化 部分特化 类型限制
	template<class T2>
	class Data<int, T2> {
	public:
		Data() {
			std::cout << "Data<T1, int>" << std::endl;
		}
	private:
		int a;
		T2 b;
	};

	template<class T1, class T2>
	class Data<T1*, T2*> {
	public:
		Data() {
			std::cout << "Data<T1*, T2*>" << std::endl;
		}
	private:
		T1 a;
		T2 b;
	};

	template<class T1, class T2>
	class Data<T1&, T2&> {
	public:
		Data() {
			std::cout<< "Data<T1&, T2&>" << std::endl;
		}
	private:
		T1 a;
		T2 b;
	};

对于上述代码,我们指定就可以通过指定的类型来进行类的调用。

int main() {
	test1::Data<int, int> d1;
	test1::Data<char, char> d2;
	test1::Data<int*, int*> d3;
	test1::Data<int&, int&> d4;
	system("pause");
	return 0;

运行结果:
在这里插入图片描述

模板的分离编译

分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

问题

但是模板不可以进行分离编译,简单来说就是模板的声明和定义不可以分开。
在这里插入图片描述
由上图我们可以发现,如果模板进行分离编译的话,那么编译器就会找不到对应的模板实例化的函数,就会产生错误。
举个例子 就像你找别人借钱,别人说借你钱,等到你找他借钱的时候找不到人了,于是就产生矛盾了,(ps:借钱伤感情)。

  • 找别人借钱就像编译器找对应的函数
  • 别人说借钱就像是找到了这个函数的声明
  • 借钱的时候你找不到人就像是找不到具体的实例化函数

解决方法

一般采用声明定义放在一个头文件里面。

总结

函数模板优点:增加代码的复用性与灵活性。
函数模板的缺点: 模板导致代码膨胀,编译时间过长,出现错误时候信息凌乱,不好定位错误。

猜你喜欢

转载自blog.csdn.net/ifwecande/article/details/106686926