模板

非类型模板参数

#define N 10
template<class T>
//静态的数组
class Array
{
private:
	T _a[N];
};
int main()
{
	Array<int> a1;    //这样子数组已经写死了!
	Array<double> a2;  //如果我们不想要10想要其他的大小那么我们就必须修改宏!这样子其实是不方便的!
	return 0;
}

==如果我们想要一个能够灵活定义的固定数组

==这时候宏就不够方便灵活了!为了能够适应这种状况!于是就有了非类型模板参数!==

template<class T,size_t N = 10>//模板可以有缺省值
class Array
{
private:
	T _a[N];
};
int main()
{
	Array<int,10> a1;   
	Array<double,10000> a2;
 //这样就可以规定我们想要的固定的数组大小了!
	return 0;
}

==非类型模板参数是一个整形常量!==

实际中C++的STL库的array里面也是使用非类型模板参数来实现了的!

image-20230329172725514

==函数模板也是可以使用这个非类型模板参数==

template<class T,size_t N>
void Func(const T& x)
{
	cout << N << endl;
}
template<class T,double N>
void Func2(const T& x)
{
	cout << N << endl;
}
int main()
{
	Func<int, 100>(1);
    FUnc2<int,1.00>(1);
	return 0;
} 

image-20230329210150404

==非类型模板参数是不可以是非整形的!==

浮点数、类对象以及字符串是不允许作为非类型模板参数的。

template<class T,size_t N>
void Func(const T& x)
{
	cout << N << endl;
}
int main()
{
	int a = 10;
	Func<int, a>(1);
	return 0;
}

image-20230329210626012

==非类型模板参数必须给的是一个常量!变量是不行的!==

模板的特化

函数模板特化

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

例如下面的例子

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};
template<class T>
bool Less(T x, T y)
{
	return x < y;
}
int main()
{
	cout << Less(1, 2) << endl; //结果正确

	Date d1(2019, 1, 1);
	Date d2(2019, 1, 2);
	cout << Less(d1, d2) << endl; //结果正确

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; //可以比较但是结果错误
	return 0;
}

image-20230329211834438

==为什么会发生这种情况呢?因为我们希望的比较的不是日期类的指针的地址的大小!而是希望比较日期类的内容!==

==所以如果能够对针对某些类型进行特殊处理那就是最好的!==

这种特殊处理我们就叫做特化!

template<class T>
bool Less(T x, T y)
{
	return x < y;
}
//对于Date*类型进行偏特化!
template<>
bool Less<Date*>(Date* x, Date* y)
{
	return *x < *y;
}

image-20230329212534258

==这样子就可以符合我们想要的行为了!==

==下面的会去调用特化的哪一个!==

偏特化是对模板的特化,是一种特殊情况!

当然了也有人回想那为什么不直接写一个对应的函数?非得要什么偏特化?

bool Less(Date* x, Date* y)
{
	return *x < *y;
}

==虽然这样子也是可以的但是,如果遇到了类模板就不行了!函数模板能用这个解决是因为有函数重载!==

类模板的特化

全特化
template<class T1, class T2>
class Base
{
public:
	Base()
	{
		cout << "Base<T1,T2>" << endl;
	}
public:
	T1 _d1;
	T2 _d2;
};
//假设如果我们想要对某种特定类进行特殊处理的话我们应该办?
//函数模板可以写一个普通的函数出来!但是类就不可以了!
//这就是对上面类的特化
template<>
class Base<double, double>
{
public:
	Base()
	{
		cout << "Base<double,double>" << endl;
	}
private:
};
//里面的内容可以自由定义,只是说如果是double的参数,那么就是用这个特化的模板类!不一样是要一样的!
int main()
{
	Base<int, int> b1;
	Base<double, double> d1;
	return 0;
}

image-20230329214119420

另一个例子就是优先级队列的插入时候的比较

image-20230330144027958

==我们看到如果我们使用的是data类的时候,我们就要显示的去写一个仿函数!==

但是有了类模板特化!我们就有了另一种新的解决办法!

==我们可以直接将less和greater等仿函数进行一次特化!我们不是对那种很大的类进行特化!我们一般是对仿函数之类的进行特化!==

上面的是全特化!就是对所有的参数进行特化!

半特化/偏特化

全特化的意思就是对全部的模板参数进行特化!

半特化就是只对部分的参数进行特化!

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

int main()
{
	Base<int, int> b1;
	Base<int ,char> b2;//优先匹配偏特化
	Base<long long ,char> b3;//优先匹配偏特化
	Base<double,char> b4;//和全特化完全匹配!那么就匹配全特化!
	return 0;
}

image-20230330151030455

==如果偏特化更加的合适!那么就会优先去匹配半特化/偏特化!而不是原模板==

只要你满足偏特化的那个特化的参数那么就会去匹配偏特化!

==如果和全特化完全匹配那么就会优先去匹配全特化而不是偏特化!==

可以简单的记忆 就像是我们吃东西!有成品就吃成品,没有成品就吃半成品!

吃半成品就看那个半成品的参数匹配的更多!(比如有三个模板参数,有一个模板参数的偏特化,和两个模板参数的偏特化。如果能匹配上两个!那么就用两个的!而不是一个!)

只有什么都不匹配的时候,才去匹配原模板!

偏特化另一个用途——限制参数类型
template<class T1, class T2>
class Base<T1*, T2*>//这是一种更加宽泛的偏特化!是对指针这个类型进行特化!
{
public:
	Base()
	{
		cout << "Data<T1*,T2*>" << endl;
	}

};
int main()
{
	Base<int, int> b1;
	Base<int*, int*> b3;
	Base<char*, char*> b4;//只要是指针都会走这个!
	return 0;
}

image-20230330151904851

template<class T1, class T2>
class Base<T1&, T2&>
{
public:
	Base()
	{
		cout << "Data<T1&,T2&>" << endl;
	}

};
int main()
{
	Base<int, int> b1;
	Base<int&,int&> b2;
	Base<double&, double&> b3;
	return 0;
}

image-20230330152224400

==引用也是可以进行偏特化!==

==特化的本质体现了编译器的参数匹配原则==——==只要有这个参数的偏特化那么就优先使用这个参数的偏特化!==

有现成的吃现成的,没有现成的吃半成品,没有半成品最后才去自己搞

模板的优缺点

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误
  3. 对于分离编译支持不好!