模板是C++泛型编程编程的基础,STL从头到尾都是模板泛型编程
函数模板
template<class T>
T add(T a, T b) { return a + b;}
int main()
{
int a = 1, b = 2;
std::cout << add(a + b) << std::endl;
return 0;
}
类模板
template<class T>
class A
{
public:
explicit A(T val) : t(val) { }
T add(T x) { return t + x; }
private:
T t;
};
int main()
{
A<int> a(10);
std::cout << a.add(5) << std::endl;
return 0;
}
几个需要注意的点
1. 类模板的和函数模板都必须定义在.h头文件中
2. 模板的实例化类型确定是在编译期间
3. 只是模板写好了,编译一般不会很多出错,出错一般会在实例化编译之后
4. 模板实例化只会实例化用到的部分,没有用到的部分将不会被实例化
模板特化
函数模板全特化
template< > // 全特化 注意语法
double add(double a, double b) { return a + b; }
int main()
{
int x = 10, y = 20;
double z = 1.1, w = 2.2;
std::cout << add(x, y) << std::endl; // 调用普通版本
std::cout << add(z, w) << std::endl; // 调用全特化版本
return 0;
}
函数模板重载(不存在偏特化)
template<class T1> // 重载版本,接收参数为指针
T1 add(T1* a, T1* b) { return *a + *b; }
int main()
{
int a = 10, b = 20;
int *x = &a, *y = &b;
add(a, b); // 调用普通模板
add(x, y); // 调用重载的模板
return 0;
}
类模板的偏特化
- 形式一
template<class T1, class T2> // 普通版本,有两个模板参数
class B { ..... };
template<class T2> // 偏特化版本,指定其中一个参数,即指定了部分类型
class B<int , T2> { ..... }; // 当实例化时的第一个参数为int 则会优先调用这个版本
- 形式二
template<class T> // 普通版本
class B { ..... };
template<class T> //这个偏特化版本只接收指针类型的模板实参
class B<T*> { ..... };
template<class T>
class B<T&> { ..... }; // 这个偏特化版本只接受引用类型的模板实参
- 形式三
template<class T> //普通版本
class B { ..... };
template<class T> // 这种只接受用T实例化的vector的模板实参.也是一种偏特化
class B<vector<T>> { ...... };
几个值得注意的地方
1. 特例化本质上是我们顶替了编译器的工作,我们帮编译器做了类型推导
2. 全特化本质上是一个实例,而偏特化本质上还是一个模板,只是原来模板的一个子集
3. 所以全特化的函数模板,本质上是实例,从而不会与函数模板产生二义性
4. 若想让用户能使用特例化版本,特例化版本必须与模板定义在同一个.h头文件中
偏特化在STL中最重要的两个应用
1. 应用在迭代器设计中,为了使迭代器既可以萃取出值类型,又可以包容原生指针
如果要通过一个迭代器就能知道它的值类型,那么一般会使用iterator_traits
迭代器萃取技术的两个核心是:
a. 在每个迭代器类中定义value_type值类型的类型成员,这样直接通过迭代器的value_type类型成员就可以知道值类型
b. 问题就在于,迭代器必须兼容原生指针,而原生指针很难被重新定义,即要在原生指针的类中添加value_type的值类型的类型成员.这时候,靠的就是类模板的偏特化了.新添加一层iterator_traits类,专门萃取迭代器的属性,然后再对iterator_traits类设计原生指针与原生引用的偏特化版本,就解决了这个棘手的问题
2.type_traits类型萃取,对待特殊类型,特殊处理,提高效率
对于没有构造函数,析构函数等的内置类型,如果与复杂类型一样,执行同样的操作,显然是效率不高的先实现一个对所有类型都设置一个最保守值的type_traits模板类,然后再对每个内置类型设置偏特化版本,内置类型设置一个更为激进的值,表明可以采取更为高效的操作来提高效率比如copy函数,如果传递的对象是一个复杂类型,那么可能只能采取最保守的处理方式,一个一个的构造;如果是内置类型,这样显然太低效,使用memcpy()可能会好一些
网友评论