C++:函数模板(理解)

目录

泛型编程

函数模板

1:函数模板概念

2:模板格式:

3函数模板原理

4:函数模板的实例化与类模板实例化

5:函数模板的匹配原则

类模板

1类模板的定义格式

2类模板的实例化

3非模板类型参数

4类模板的特化

5类模板特化之类型萃取

6模板分离编译


泛型编程

泛型编程最初提出时的动机很简单直接:发明一种语言机制,能够帮助实现一个通用的标准容器库。所谓通用的标准容器库,就是要能够做到,比如用一个List类存放所有可能类型的对象这样的事;泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。

泛型即是指具有在多种数据类型上皆可操作的含义,与模板有些相似。STL巨大,而且可以扩充,它包含很多计算机基本算法和数据结构,而且将算法与数据结构完全分离,其中算法是泛型的,不与任何特定数据结构或对象类型系在一起。

函数重载的缺点:

  1. 重载的函数仅仅只是类型不同,代码的复用率比较低
  2. 代码的维护性比较低,一个出错可能所有的重载均出错
  3. 如果函数的返回值类型不同,不行形参重载
  4. 只要有新的类型出现,就需要增加对应的函数

所以c++中存在这样一个模具,通过这个不同的类型生成不同的具体代码。

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础

函数模板

1:函数模板概念

函数模板代表一个函数族,该函数模板与类型无关,使用时被参数化,根据实参类型产生函数的特定类型版本

2:模板格式:

                template<typename T1,typename T1,....>

                返回值类型 函数名 (参数列表){  }

template<typename T>
void Swap(T& left,T& right)
{
  T temp = left;
  left = right;
  right = temp;
}

注意:typename是用来定义模板参数关键字,也可以使用class,建议尽量使用typename

3函数模板原理

模板是一个蓝图,它本身不是一个类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,这个过程称之为实例化!

编译器需要根据传入的实参类型,来推演生成对应类型的函数应用

4:函数模板的实例化与类模板实例化

不同类型的参数使用函数模板时,称之为实例化。模板参数实例化分为两种:隐式实例化和显示实例化

 1:隐式实例化

模板:定义:函数模板隐式实例化指的是在发生函数调用的时候,如果没有发现相匹配的函数存在,编译器就会寻找同名函数模板,如果可以成功进行参数类型推演,就对函数模板进行实例化。

类:类模板隐式实例化指的是在使用模板类时才将模板实例化

2:显示实例化

对于函数模板而言,不管是否发生函数调用,都可以通过显示实例化声明将函数模板实例化,格式为:


template 函数返回类型 函数模板名<实际类型列表>(函数参数列表)

template class 类模板名<实际类型列表>

函数模板:对于函数模板而言,不管是否发生函数调用,都可以通过显示实例化声明将函数模板实例化

类模板:对于类模板而言,不管是否生成一个模板类的对象,都可以直接通过显示实例化声明将类模板实例化

如果两个参数类型不同,T推演的类型只有一个,编译器无法处理而报错Swap<int>(a,b)

5:函数模板的匹配原则

函数的所有重载版本的声明都应该位于被调用函数之前

int Max(const int& left ,const int& right);
template<class T>
T Max(const T& left, const T& right)
{
return (left>right)? left:right;
}
template<class T>
T Max(const T& left, const T& mid, const T& right)
  1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调用时会优先调用非模板函数而不会从模板生出一个实例(如果模板可以产生一个具有更好匹配的函数,那么选择模板)
  3. 显示指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,而且所有模板参数都应该根据实参演绎出来
  4. 函数模板不允许自动类型转换,但普通函数可以进行自动类型转换

类模板

1类模板的定义格式

template

class 类模板名

{...};

//动态顺序表
template<typename T>
class Vector
{
public:
	Vector;
	~Vector()
	{
		delete[] _data;
	}
private:
	int _size;
	int _capacity;
	T* _data;
};
//注意:类模板中函数放在类外进行定义时,需要加模板参数列表
template<typename T>
Vector<T>::Vector()
	:_size(0)
	, _capacity(10)
	, _data(new T[_capacity])
{}

void test1()
{
	Vector<int>s1;
	Vector<double>s2;
}

2类模板的实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在模板名字后跟<>,然后将实例化的类型放在<>中即可,模板类名字不是真正的类

而实例化的结构才是真正的类

	Vector<int>s1;
	Vector<double>s2;

3非模板类型参数

非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数

//静态顺序表 
template<typename T,szie_t N = 10>
class Array
{
pubilc:
	Array()
		: _size(0)
	{}
private:
	T _array[N];
	int _size;
};

void Test()
{
   Array<int>s1;
   Array<double>s2;
}

注意:浮点数和类对象(自定义类型)不能做非模板类型参数

4类模板的特化

模板对于大部分的类型都可以直接处理,但有些类型处理可能会出现问题,对于不能直接处理的类型,

需要对其进行特殊化处理,因此在进行特化时,需要先给出一个模板类,然后对其进行某些类型的特殊化处理。

类模板的特化分为:全特化和偏特化

以此为例:

template<typename T1,typename T2>
class Data
{
pubilc:
	Data
	()
	{};
private:
	T1 _d1;
	T2 _d2;
};

1:全特化

全特化是将模板参数列表中的所有类型参数均具体化

//全特化
template<int ,int>
class Data
{
pubilc:
	Data
	()
	{};
private:
	T1 _d1;
	T2 _d2;
};

将来如果将Data类模板的两个参数全部实例化为int时,编译器会优先选择特化版本,而不会去使用基础的类模板再实例化

2偏特化

偏特化是将类模板中的部分参数具体化称之为部分特化或者局部特化,或者是针对模板参数更进一步的条件限制所设计

的一个特化版本

//偏特化
template<typename T ,int>
class Data
{
pubilc:
	Data
	()
	{};
private:
	T1 _d1;
	int _d2;
};

让模板参数限制更加严格!

只要是第二个参数为int型都会使用上述模板

5类模板特化之类型萃取

类型萃取使用模板技术来萃取类型(包含自定义类型和内置类型)的某些特性,用以判断

该类型进行特殊的处理用来提高效率或者其他

问题:如何实现一个通用拷贝?

方式一:

template<class T>
void Copy(T* dst, const T* src, szie_t size)
{
	memcpy(dst,src,sizeof(T)*size);
}

上述代码虽然对于任意类型的空间都可以拷贝,但是如果拷贝自定义类型对象就有可能出错,因为自定义类型对象有可能会涉及深拷贝

而memcpy属于浅拷贝,如果涉及到深拷贝,就只能用赋值

方式二:

template<class T>
void Copy(T* dst,T* src,size_t size)
{
   for(size_t i = 0;i < size;++i)
   {
       dst[i] = src[i] ;
   }
}

用循环赋值的方式虽然可以,但是代码效率比较低,而c和c++最大的优势就是效率高,

能否当遇见内置类型就用memcpy来拷贝,遇见自定义类型就用循环赋值方式来做

方式三:

template<class T>
void Copy(T* dst, const T* src, size_t size,bool IsPODType)
{
	if (IsPODType)
	{
		memcpy(dst, src, sizeof(T)*size);
	}
	else
	{
		for (size_t i = 0;i < size;++i)
		{
			dst[i] = src[i];
		}
	}
}

POD(Plain Old Data)(平凡类型):

POD数据类型主要用来解决C++与C之间数据类型的兼容性,以实现C++和C函数的交互

简单理解为:C++ 中与 C兼容的类型,可以按照 C 的方式处理

通过多增加一个参数,就可以将两种拷贝的优势体现结合起来。但缺陷是:用户需要根据所拷贝

元素的类型去传递第三参数,那出错的可能性就增加,那能否让函数自动去识别所拷贝类型是

内置类型还是自定义类型呢?
方式四:

因为内置类型的个数是确定的,可以将所有的内置类型聚合在一起,如果能够将所拷贝对象的类型确定下来

在内置类型中查找是否存在即可确定所拷贝的类型是否为内置类型

// 此处只是举例,只列出个别类型
bool IsPODType(const char* strType)
{ 
     const char* arrType[] = {"char", "short", "int", "long", "long long", 
                              "float", "double","long double"}; 
for(size_t i = 0; i < sizeof(array)/sizeof(array)/sizeof(array[0]); ++i)
{
        if(0 == strcmp(strType, arrType[i]))
        return true;
}
        return false;
}

template<class T>
void Copy(T* dst, const T* src, size_t size)
{
    if(IsPODType(typeid(T).name()))
       memcpy(dst, src, sizeof(T)*size);
    else
     {
     for(size_t i = 0; i < size; ++i)
     dst[i] = src[i];
     }
}

通过typeid所确认的拷贝对象的实际类型,然后在内置类型集合中枚举其是否出现过,既可以确认所拷贝的元素类型

为内置类型或者为自定义类型,但缺陷是:枚举需要将所有的类型遍历一遍,每次比较字符串,效率低

方式五:

为了将内置类型与自定义类型区分开,给出以下两个类分别代表内置类型与自定义类型。

// 代表内置类型
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<short>
{
    typedef TrueType IsPODType;
};

。。。。。等等

通过对TypeTraits类模板重写改写方式四中的Copy函数模板,来确认所拷贝对象的实际类型。

/*
T为int:int实例化TypeTraits模板类对应的TypeTraits<int>已经特化过,程序运行时就会使用已经特化过的
TypeTraits<int>,该类中的IsPODType刚好为类TrueType,而TrueType中Get函数返回true,内置类型走
memcpy拷贝
string实例化TypeTraits模板类对应的TypeTraits<string>没有特化过,程序运行时使用TypeTraits类模
板,该类模板中的IsPODType刚好为类TrueType,而TrueType中Get函数返回true,内置类型走memcpy拷贝
*/
template<class T>
void Copy(T* dst, const T* src, size_t size)
{
   if(TypeTraits<T>::IsPODType::Get())
   memcpy(dst, src, sizeof(T)*size);
   else
   {
   for(size_t i = 0; i < size; ++i)
   dst[i] = src[i];
   }
}

6模板分离编译

猜你喜欢

转载自blog.csdn.net/W_J_F_/article/details/83187491