1

C++之模板详解(三千字长文详解)

 1 year ago
source link: https://blog.51cto.com/u_15835985/7084812
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

[TOC]

非类型模板参数

#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里面也是使用非类型模板参数来实现了的!

C++之模板详解(三千字长文详解)_函数模板

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

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;
} 
C++之模板详解(三千字长文详解)_函数模板_02

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

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

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;
}
C++之模板详解(三千字长文详解)_Less_03

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

模板的特化

函数模板特化

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

例如下面的例子

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;
}

C++之模板详解(三千字长文详解)_函数模板_04

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

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

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

template<class T>
bool Less(T x, T y)
{
	return x < y;
}
//对于Date*类型进行偏特化!
template<>
bool Less<Date*>(Date* x, Date* y)
{
	return *x < *y;
}
C++之模板详解(三千字长文详解)_Less_05

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

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

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

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

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;
}

C++之模板详解(三千字长文详解)_偏特化_06

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

C++之模板详解(三千字长文详解)_偏特化_07

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

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

C++之模板详解(三千字长文详解)_函数模板_08

我们可以直接将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;
}

C++之模板详解(三千字长文详解)_偏特化_09

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

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

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

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

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

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

偏特化另一个用途——限制参数类型
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;
}

C++之模板详解(三千字长文详解)_函数模板_10
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;
}
C++之模板详解(三千字长文详解)_函数模板_11

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

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

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

模板的优缺点

【优点】

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

【缺陷】

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK