目录
一. 内容
上次我们只是简单的谈了一下模板,我们只是可以简单的使用一下模板的泛型编程,这次我们看一模板的进阶,模板特化。
这次我们先看一下函数模板的特化,然后我们看类模板的特化,主要是一些我们之前不怎么使用的语法,下面我们来看一下。
二. 函数模板_特化
函数模板上一次我们就说过了,但是我们还是在看一下,由普通的函数模板引到函数模板的特化。
我们现在想要实现对一个数组的打印 print 函数,那么我们怎么实现?
1. typename
我们可以给 print 函数传一个 vector 的对象,然后我们使用 vector 的迭代器来打印~
void print(const vector<int>& v)
{
std::vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
}
那么我们现在要是还想要实习一个 string 的呢?
我们可以使用模板,我们将我们的 print 的参数当作一模板,我们下面试一下。
template<class Container>
void print(const Container& v)
{
Container::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
}
那么我们就可以这样,我们可以将一个类型传过去,然后用该类型的 const_iterator 来进行遍历。
但是实际上这里是编译不过去的,我们看一下报错信息。
为什么会这样呢?
我们既然传过去的是一个类型,那么我们想下面这样取的话,我们可能会取到什么呢?我们可能取到的一个 static 的变量,我们还可能取到的是一个我们的类内的一个类,所以我既然可以取到是 static 的变量那么我们的语法就是错误的,由于编译器它也并不知道我们到底能取到什么,所以就直接报错了,那么我们怎么解决呢?
我们在前面加 typename 这个的意思是告诉编译器说这个是个类型,所以我们就可以编译通过了,那么我们现在加上去看一下是否可以成功。
template<class Container>
void print(const Container& v)
{
typename Container::const_iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
}
加上去之后就可以了,这里我们就不把测试的代码放出来了。
2. 非类型模板参数
我们现在想要实现一个静态的数组,并且我们的这个数组还是一个对象,那么我们该怎么实现呢?
我们由几种方法,其中一种就是 define 定义一个常量,然后我们将该常量传入到我们想要的数组中。
#define N 10
template<class T>
class Array
{
public:
private:
T _a[N];
};
但是这个我们每一次定义的都是 10 个大小,我们要是想给大一点,我们就需要把所有的 Array 的对象的数组都给大,那怎么办呢?
我们下面的这个解决方法就是特意解决这种问题的,非类型模板参数。
我们可以在模板里面给一个整型的参数,我们就可以指定的传入这个参数。
template<class T, size_t N>
class Array
{
public:
private:
T _a[N];
};
void test()
{
Array<int, 10> a;
}
这个就是我们的非类型模板参数,实际上我们的非类型模板参数就是解决类型的问题的,而且我们的非类型模板参数的限制比较多,我们下面可以看一下。
非类型模板参数只能是整型家族的。
template<class T, double N>
class Array
{
public:
private:
T _a[N];
};
如果是 double 那么就是直接报错了,但是这里的代码看不出来,可以自己取试一下
非类型模板参数是一个常数,所以我们不能修改。
template<class T, size_t N>
class Array
{
public:
void fun()
{
N = 10;
}
private:
T _a[N];
};
void test()
{
Array<int, 10> a;
a.fun();
}
我们调用的话就直接报错了,这里还是显示不出来但是可以自己取试一下。
3. 函数模板特化
1). 全特化
为什么前面说 typename 呢? 因为这个 typename 和 class 的一个区别。
下面我们来说特化
我们来看下面的一段代码,这段代码会输出什么呢?
template<class T1, class T2>
int Less(T1 x, T2 y)
{
return x < y;
}
void test2()
{
cout << Less(1, 2) << endl;
cout << Less(1.1, 2) << endl;
cout << Less(1, 2.2) << endl;
}
其实这个我们是没有太多要问题的,那么我们如果传入的是一个 int* 的类型呢?
template<class T1, class T2>
int Less(T1 x, T2 y)
{
return x < y;
}
void test2()
{
int x = 1, y = 2;
cout << Less(&x, &y) << endl;
}
这个我们知道我们传入的是一个指针,我们肯定是以它的地址大小比较的,所以这时候我们就可以进行模板的特化。
template<class T1, class T2>
int Less(T1 x, T2 y)
{
return x < y;
}
template<>
int Less<int*, int*>(int* x, int* y)
{
return *x < *y;
}
这里先看代码,要是直接说的话怕是说不明白的,下面的那个就是模板特化的一个语法,所以我们不需要取纠结。
我们既可以特化指针,我们还可以特化其他的,我们知道函数调用就是调用最接近的函数。我们看一下其它的特化。
template<>
int Less<int, double>(int x, double y)
{
return x < y;
}
void test2()
{
cout << Less(1, 1.1) << endl;
}
我们上面讲的是特化里面的全特化,我们还有就是半特化。
2). 偏特化
偏特化也就是半特化顾名思义,就是只特化里面部分类型。
template<class T1>
int Less<T1, double>(T1 x, double y)
{
return x < y;
}
就是这样我们只特化一半的类型。
上面只是我们的函数的特化,那么下面我们看一下类模板特化。
4. 类模板特化
1). 全特化
我们现在有一个 Date 类型的对象,我们现在想要对它进行模板的特化。
template<class T1, class T2>
class Date
{
public:
Date()
{
cout << "Date(T1, T2)" << endl;
}
private:
int _year;
int _month;
int _day;
};
我们现在想要对其进行特化一个 int, int 类型的
template<>
class Date<int,int>
{
public:
Date()
{
cout << "Date(int, int)" << endl;
}
private:
int _year;
int _month;
int _day;
};
我们在看一下 int, double 的
template<>
class Date<int, double>
{
public:
Date()
{
cout << "Date(int, double)" << endl;
}
private:
int _year;
int _month;
int _day;
};
上面这个就是全特化,我们特化就是语法,所以我们记住怎么样使用,下面我们看一下偏特化。
2). 偏特化
我们就是特化部分类型
template<class T1>
class Date<T1, double>
{
public:
Date()
{
cout << "Date(T1, double)" << endl;
}
private:
int _year;
int _month;
int _day;
};
我们的特化实际上不是只能对参数进行修改,我们还可以对类型进行限制。
我们可以对类型进行限制为指针,也可以限制为引用。
template<class T1, class T2>
class Date<T1*, T2*>
{
public:
Date()
{
cout << "Date(T1*, T2*)" << endl;
}
private:
int _year;
int _month;
int _day;
};
我们上面这个是指针,我们还可以限制为是引用,所以我们的模板的偏特化,还可以是类型的限制。
上面就是我们今天的模板进阶~
模板还是需要多使用才能孰能生巧,所以还是希望可以多使用~