C++模板特化和偏特化(二)

一、函数模板

(1)函数的匹配优先级:

  • 普通函数;
  • 重载函数;
  • 普通函数模板;
  • 全特化函数模板。
  • 函数模板不允许使用偏特化,如有需求,改成模板函数的重载。

2)函数模板特化

      函数模板特化主要的用途都是对于特定的类型,指定特定的处理方式。函数模板特化的意义在于如果有与实参更加匹配的特例化版本,编译器将会选择特例化版本

例:普通函数模板

template<class T>
T add(T a, T b) { 
	return a + b; 
}

int main()
{
	int a = 1, b = 2;
	std::cout << add(a, b) << std::endl;
	return 0;
}

注:当实例化函数模板时,编译器会自动进行实参类型推导,上面类型T就被自动推导为int类型。

 

例:特化函数模板

类文件

// 函数模板特化
class SpeMethond
{
public:
	SpeMethond() {}
	~SpeMethond() {}

	// 普通函数模板
	template<typename T>
	T add(T x, T y) {
		T s = x + y;
		cout << "普通函数模板(一):" << x << " + " << y << " = " << s << endl;
		return s;
	}

	// 全特化函数模板,必须有对应的普通函数模板版本
	template<>
	int add(int x, int y) {
		int s = x + y;
		cout << "全特化函数模板:" << x << " + " << y << " = " << s << endl;
		return s;
	}

	// 普通函数模板(二)
	template<typename T1, typename T2>
	T2	add(T1 x, T2 y) {
		cout << "普通函数模板(二):" << x << " + " << y << " = ";
		return x + y;	// 注意此处的强制转换
	}

	// 函数模板没有偏特化,除非重载
	//template<typename T>
	//T add(int x, T y) {
	//	cout << "普通函数模板(一):" << x << " + " << y << " = " << s << endl;
	//	return s;
	//	return x + y;
	//}

	// 重载版本,接收参数为指针
	template<class T1>
	T1 add(T1* a, T1* b) { 
		cout << "重载版本:" << *a << " + " << *b << " = ";
		return *a + *b; 
	}
};

调用文件:

// 函数特化
int  main()
{
	SpeMethond speMeth;
	int a = 3, b = 6;
	speMeth.add(a, b);	// 调用全特化函数模板

	double m = 2.3, n = 8.2;
	speMeth.add(m, n);	// 调用普通函数模板(一)

	cout << speMeth.add(a, m) << endl;	// 调用普通函数模板(二)

	int *x = &a, *y = &b;
	cout << speMeth.add(x, y) << endl;	// 调用重载的模板

	system("pause");
	return 0;
}

输出:

       函数模板的全特化的用法之一,比如:std命名空间不允许增添任何新的函数重载,但是可以增添模板函数的全特化。

 

二、类模板

实例化类模板必须要指定类型,编译器无法为类模板自动推导类型。

类模板类型:普通类模板、全特化类模板、偏特化类模板。

1、普通类模板

例:

template<class T>
class A
{
public:
	explicit A(T val) : t(val) { }

	T add(T x) { 
		return t + y; 
	}

private:
	T t;
};

注意:实例化类模板必须要指定类型,编译器无法为类模板自动推导类型。

 

2、全特化类模板

       类模板全特化比较好理解,跟函数模板一样,全特化是一个实例,当编译器匹配时会优先匹配参数一致的实例。

例:

template<>
class A<char*>		// 当用char*类型来实例化类模板A时,将会优先调用这个全特化实例
{                    
public:
	explicit A(char* val) : t(val) { }

	char* add(char* a, char* b) { 
		return strcat(a, b); 
	}

private:
	char* t;
};

 

3、偏特化类模板

       类模板的偏特化有多种形式,类模板偏特化本质上都是指定部分类型,让偏特化版本成为普通版本的子集,若实例化时参数类型为指定的类型,则优先调用特例化版本。

(1)形式1

template<class T1, class T2>      // 普通版本,有两个模板参数
class B { 
	//..... 
};

template<class T2>		// 偏特化版本,指定其中一个参数,即指定了部分类型
class B<int, T2> {		// 当实例化时的第一个参数为int 则会优先调用这个版本
	//..... 
};	

(2)形式2

template<class T>     // 普通版本
class B { 
	//..... 
};

template<class T>	//这个偏特化版本只接收指针类型的模板实参 
class B<T* > { 
	//..... 
};

template<class T>
class B<T&> {		// 这个偏特化版本只接受引用类型的模板实参
	//..... 
};

(3)形式3

template<class T>    //普通版本
class B { ..... };

template<class T>	// 这种只接受用T实例化的vector的模板实参.也是一种偏特化
class B<vector<T>> { 
	//...... 
};

 

注意:

  • 若想让用户能使用特例化版本,特例化模板版本必须与普通模板定义在同一个.h头文件中。
  • 特例化本质上是我们顶替了编译器的工作,我们帮编译器做了类型推导,如果特化和非特化模板同时存在时,编译器认为我们有更好的实现版本,所有会优先选择特化模板的版本。
  • 全特化本质上是一个实例,而偏特化本质上还是一个模板,只是原来模板的一个子集。
  • 模板的实例化类型确定是在编译期间。
  • 模板实例化只会实例化用到的部分,没有用到的部分将不会被实例化。
  • 只是模板写好了,编译一般不会很多出错,出错一般会在实例化编译之后。
原创文章 99 获赞 68 访问量 3万+

猜你喜欢

转载自blog.csdn.net/King_weng/article/details/105363733