C++:模板总结

前提:我们要实现一个通用的交换函数swap,就是让它适用于任何一个类型。比如:int,char,double......,但是我们要写很多交换的函数,对swap函数进行重载。那我们如何解决这个问题?可不可以给编译器一个模子,让编译器来给我们实现不同类型的转化?

函数模板

1.概念:函数模板代表了一个函数家族,在使用时被参数化,根据实参类型产生特定的函数。

2.函数模板格式

template<class T>//不能用struct代替class
T add(const T& a,const T&b)
{
	return a + b;
}
int main()
{
	int a = 1, b = 2;
	double c = 1.2, d = 2.4;
	cout << add(a, b) << endl;;
	cout<<add(c, d)<<endl;
	system("pause");
	return 0;
}

3.函数模板的原理

在编译器编译阶段,编译器会根据传入的实参类型来推演出对应生成的类型来调用,就是将T确定成相应的函数类型,比如:int,double...。

4.函数模板的实例化

用不同类型的参数使用模板时,就是对函数模板的实例化。实例化可以分成两种:隐形实例化和显性实例化。

        (1)隐形实例化:让编译器来推演出模板参数的实际类型。

template<class T>
T add(const T& a,const T&b)
{
	return a + b;
}
int main()
{
	int a = 1, b = 2;
	double c = 1.2, d = 2.4;
	add(a, b);
	add(c, d);
	//add(a, d);error C2782: “T add(const T &,const T &)”: 模板 参数“T”不明确	

	add(a, (int)d);
	system("pause");
	return 0;
}

上述代码为何会出错?

因为在编译阶段,看到实参a时打算将T推演成int类型,然后又遇到实参d,是double类型,所以编译器就不知道是将T推演成哪一个参数,就会很直接的报错。

所以,这里的错误处理方式有两种:1.自己强转;2.使用显示实例化

            (2)显示实例化:在函数名字后的<>中指定模板参数的实际类型

template<class T>
T add(const T& a,const T&b)
{
	return a + b;
}
int main()
{
	int a = 1, b = 2;
	double c = 1.2, d = 2.4;
        //显示的实例化
	add<int>(a, d);
	system("pause");
	return 0;
}

5.模板函数的匹配原则

(1)一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板任然可以被实例化。

(2)如果一个非模板函数和同名的函数模板同时存在,那么编译器会优先调用非模板参数。

template<class T1,class T2>//函数模板
T2 add(const T1& a,const T2&b)
{
	return a + b;
}

int add(const int a, const int b)//非模板函数
{
	return a+b;
}
int main()
{
	int a = 1, b = 2;
	double c = 1.2, d = 2.4;

	cout<<add(a, b)<<endl;//调用非模板函数
	cout << add(a, c) << endl;/调用函数模板
	system("pause");
	return 0;
}

3.模板函数不允自动类型转化,但是普通函数可以进行自动类型转化。

类模板

用类模板实现顺序表的一部分

template<class T>//类模板的格式
class myvector
{
public:
	myvector()
		:data(new T[10])
		, size(0)
		, capacity(10)
	{

	}
	void PushBack(const T& mydata)
	{
		data[size++] = mydata;
	}
	void PopBack()
	{
		--size;
	}
	int Size()
	{
		return size;
	}
	T& operator[](int pos)
	{
		assert(pos < size);
		return data[pos];
	}

	~myvector()
	{
		if (data)
		{
			delete[] data;
		}
	}

private:
	T*data;
	int size;
	int capacity;
};

int main()
{
	myvector<int> a;//类模板的实例化
	a.PushBack(1);	
        a.PushBack(2);
	a.PushBack(3);
	a.PushBack(4);
	for (int i = 0; i < a.Size(); i++)
		cout << a[i]<< " ";
	system("pause");
	return 0;
}

类模板的实例化和函数模板的实例化不同,类模板实例化需要在类模板名字后加<>,然后将类型放在<>即可。

非类型的模板参数

模板参数有两种,一种是类型参数,另一种是非类型参数。

类型参数就是在模板参数列表中,跟class或者typename之类的参数类型名称

非类型形参就是用一个常量作为类模板的一个参数,在类模板中可以将这个参数当成常量来使用。

//定义一个静态的数组
template<class T,int N=10>
class ARRAY
{
public:
	T& operator[](int index)  
	{ 
		return ARRAY[index]; 
	}      
	const T& operator[](int index)const    
	{
		return ARRAY[index]; 
	}       
	int Size()const   
	{ 
		return _size; 
	}       
	bool Empty()const   
	{ 
		return 0 == _size; 
	}

private:
	T _array[N];//N在类中是个常量,可以直接使用
	int _size;
};

需要注意的是:浮点型(float)、类对象和字符串是不允许作为非类型模板参数的,非类型的模板参数必须在编译器就能确认结果,可以将它定义成一个宏的常量。

模板的特化

通常情况下,模板可以实现一些与类型无关的代码,但是对于一些特殊类型的可能会得到一些错误的结果。所以我们就需要对模板进行特化,就是在原模版的基础上,针对特殊类型所进行的特殊的实现方式。模板的特化分成:函数模板特化和类模板特化。

1.函数模板的特化

步骤:(1)必须有一个基础的函数模板

          (2)在关键字templaste后边加<>

          (3)<>中需要加指定的类型

          (4)参数列表必须和基础函数模板参数类型完全相同

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

其实这么实现没有必要,我们可以采用更加简单的直接写出函数就可以。

2.类模板的特化

类模板的特化分成两种,一种全特化,另一种是偏特化。

(1)全特化就是模板参数列表中所有的参数都被确化

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

template<>
class Data<int ,char>//全特化
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	int _d1;
	char _d2;
};

(2)偏特化是对模板参数进行进一步的条件限制的特化版本

             第一种:部分特化

template<class T>
class Data<T, char>//对第二个参数类型特化成char
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	T _d1;
	char _d2;
};

           第二种:对参数更进一步的条件限制

template <typename T1, typename T2>//两个参数特化成指针类型
class Data<T1*, T2*>
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};

模板特化的应用之类型萃取

1.实现一个通用的拷贝函数

拷贝我们可以使用memcpy函数,也可以用for循环一个一个的赋值,但是我们需要了解到这两种拷贝的优缺点。

memcpy函数可以对任意的内置类型进行拷贝,但是拷贝自定义类型的时候就会出错,而且它是浅拷贝的方式。

循环赋值的方式虽然可以对自定义类型进行拷贝,但是代码的效率比较差。

所以我们在实现通用的拷贝函数的时候,可以将这两种拷贝方式结合在一起。

bool isPOD(const char *name)
{
	const char *t[] = { "long", "char", "short", "int", "float" };
	for (size_t i = 0; i < sizeof(t) / sizeof(t[0]); i++)
	{
		if (strcmp(t[i], name) == 0)
			return true;
	}
	return false;
}
template<class T>
void Copy( T* dst,T* src,size_t size)
{
	if (isPOD(typeid(T).name()))//判断是不是内置类型
		memcpy(dst,src,size);
	else
	{
		for (size_t i = 0; i < size; i++)
			dst[i] = src[i];
	}
}

int  main()
{
	string strarr1[3] = { "11", "22", "33" };
	string strarr2[3];
	Copy(strarr2, strarr1, 3);//用循环赋值
	int a = 100;
	int b = 0;
	Copy(&b, &a, 4);//用memcpy
	system("pause");
	return 0;
}

2.类型萃取

struct TrueType//内置类型
{
	static bool Get()
	{
		return true;
	}
};

struct FalseType//自定义类型
{
	static bool Get()
	{
		return false;
	}
};
template<class T> 
struct TypeTraits
{
	typedef FalseType IsPODType;
};

template<> struct TypeTraits<char>  { typedef TrueType   IsPODType; };
template<> struct TypeTraits<int>   { typedef TrueType   IsPODType; };
template<> struct TypeTraits<short> { typedef TrueType   IsPODType; };
template<> struct TypeTraits<float> { typedef TrueType   IsPODType; };
template<> struct TypeTraits<long>  { typedef TrueType   IsPODType; };
//......
template<class T>
void Copy(T* dst, T* src, size_t size)
{
	if (TypeTraits<T>::IsPODType::Get())
		memcpy(dst, src, size);
	else
	{
		for (size_t i = 0; i < size; i++)
			dst[i] = src[i];
	}
}

int main()
{
	string strarr1[3] = { "11", "22", "33" };
	string strarr2[3];
	Copy(strarr2, strarr1, 3);
	int a = 100;
	int b = 0;
	Copy(&b, &a, 4);
	system("pause");
	return 0;
}

我们使用两个结构体区分开了是自定义类型还是内置类型?

当我们拷贝strarr1和strarr2的时候,由于TypeTraits<string>没有被特化,所以该类模板中的IsPODType刚好为类FalseType,而FalseType中Get函数返回true,自定义类型使用赋值方式拷贝 

当我们拷贝a和b的时候,由于TypeTraits<int>被特化,所以该类模板中的IsPODType刚好为类TrueType,而TrueType中Get函数反悔true,证明是内置类型,内置类型需要用memcpy

猜你喜欢

转载自blog.csdn.net/oldwang1999/article/details/84677995