
一、模板特化
通过前面的文章分析,相信对模板已经不再陌生。至少对模板的相关的基础知识已经有一个明白的认识。模板最基础的应用之一就是模板的特化。模板的特化为分全特化和偏特化。可以简单理解为一个是全部给出了具体的类型;另外一个只是给出了部分的具体类型。那么,在这种特化的过程中,参数的特化顺序是什么呢?
二、模板参数
无论是写类模板还是函数模板,都会发现其中有着或多或少的参数,类似下面:
template <typename T,typename U>
classDemo{};
template<typename T,typename U>
voidcreate(T t,U u){}
而有些情况下还存在着模板的模板参数等特殊情况。当模板的参数变得复杂起来时,如何对模板参数有一个清晰的把控是非常重要的。这对于模板参数特化的细节有着重要的作用。它可以有效的防止出现对特化过程的误用或理解错误。
三、模板参数特化的顺序
在C++的模板特化过程中,按照模板特化的分类可以分为:
全特化
全特化很好理解,因为它处理于全部参数特化情况。顺序已经被显式的指定了,后台的顺序,其实对开发者感觉不到。但一定要明白,模板的参数特化是按位置匹配,而非按名称或以前学习的函数参数的压栈顺序等规定,看下面的例子://参数顺序:T, U
template <typename T, typename U>
classTest {};
//T:int, U:double
template <>
classTest<int, double> {};偏特化
偏特化的情况也是如此。如果说//参数顺序:T, U
template <typename T, typename U>
classTest {};
// T:未特化,U:int
template <typename T>
classTest<T, int> {};
// T:int,U:未特化
template <typename U>
classTest<int, U> {};
几种特殊情况:
变参模板
变参模板的要求其实也是符合前面的参数特化顺序的(除非新标准修改),即特化的参数要在变参包前面,或者说变参包始终位于模板参数列表的末尾。如:template <typename T, typename... Args>
classTest {};
// 偏特化:T 固定,Args在后
template <typename... Args>
classTest<int, Args...> {};模板的模板参数
这种情况仍然要保持与普通模板参数一样严格的参数声明顺序一致性。如:template <typename T, template <typename> classVec>
classTest {};
template <template <typename> classVec>
classTest<int, Vec> {};模板参数的默认参数
拥有默认参数时,模板参数的特化顺序仍然不受影响。但需要说明在偏特化,要显式的保证默认参数的位置编译期可见。即要么手动写全,要么编译期可推导
这里有一个细节的问题,可能有些对模板显式实例化或实例化与模板参数的特化过程有一些混淆。如果单纯从模板参数的特化顺序来看,它们是一致的。但一定要明白它们二者是不同的概念。
通过上述的分析,其实可以用一句话来表明模板参数的特化顺序即“必须严格按照主模板的声明顺序”进行。可以简单理解为按照模板参数中的逗号的前后顺序一致,有些初学者可能会联想一些在声明后的代码中主动的进行顺序修改,这是不必要的,也不可能实现的。看一下下面的例子:
template <typename T> classTest<T, int> {};
template <typename U> classTest<U, int> {};
这两种在编译器中会产生重定义,大家可以仔细想一想为什么?其实就是刚刚提到的,不要想当然的在顺序声明后试图修改这个顺序。
四、例程
在看了上面的分析后,再给大家看一个很容易中招的例程:
#include<iostream>
template <typename T, typename U> classTest {};
template <typename T> classTest<T, int> {
public:
voidprint(){ std::cout << "T int 111" << std::endl; }
};
template <typename U> classTest<int, U> {
public:
voidprint(){ std::cout << " int U 222" << std::endl; }
};
intmain(){
Test<char, char> t;
Test<int, char> t2;
t2.print(); // 输出 " int U 222"
Test<double, int> t1;
t1.print(); // 输出 "T int 11111"
// Test<int, int> tt;//如果打开注释的这行,会出现什么?
return0;
}
上面的程序是可以编译运行的。但是要按注释启用这行代码,会报“ambiguous partial specialization”。意思是偏特化模糊匹配,无法正常实例化。大家不要认为这个简单,就没人犯这种错误。在复杂的合作开发中,极有可能有人写出这种前后互相掣肘的代码。
五、总结
C++模板编程是C++开发一个非常灵活也非常难以深入掌握的一个技术。其中的细节和特异性的点非常多。但不要担心,随着标准的不断演进以及相关技术的不断演进,这些问题都会慢慢得到解决。大家只要掌握好相关的技术基础,遇到问题后不要慌。因为这些技术都是从基础的技术上扩展和抽象出来的。
夜雨聆风