Learning to be Giant.

Partial Template Specification

|

看大牛写的代码就是非常长知识,今天下午有丧心病狂地看了一下午研究了一下午的C++ template的一个语言特性,发现中文的文章几乎没有,所以既然我花了这么久来研究这个,就斗胆来发一篇。有部分内容其实是翻译的。

参考:http://en.cppreference.com/w/cpp/language/partial_specialization

Partial Template Specification实现了通过对于模板类模板参数不同程度的细化而实现不同的模板类功能的目的。首先来看一个例子。

template <class PixType, bool B = PixType::is>
class Test {
public:
    void print() {
        cout << "PixType B=false" <<endl;
    }
};

template <class PixType>
class Test<PixType,true> {
public:
    void print() {
        cout << "PixType B=true" <<endl;
    }
};

class PT {
public:
    static const bool is = false; // here is the magic
};

int main() {
    Test<PT> t;
    t.print();
}

现在,当我们执行这段程序的时候,程序会输出PixType B=false,而当我们改变is的值变为true的时候,程序输出就是PixType B=true了。这一个例子使用了C++的Partial Template Specification。

如何定义

Partial Template Specification(以下简称PTS)事实上跟一般的template差不多。大家注意观察上面的第二个模板类,有没有注意到在类名Test后面多了一个尖括号,里面写了模板参数和一个非类型(Non-type)的模板参数?这个就是PTS,而第一个模板类则被称为主模板(Primary template)。事实上,PTS就是通过对于主模板当中的参数进行细化而产生的,从而可以在不同的参数条件下实现不同的类功能。

更加规范一点的定义:

template < parameter-list > class-key class-head-name < argument-list > declaration

template<class T1, class T2, int I>
class A {}; // primary template
 
template<class T, int I>
class A<T, T*, I> {}; // partial specialization where T2 is a pointer to T1
 
template<class T, class T2, int I>
class A<T*, T2, I> {}; // partial specialization where T1 is a pointer
 
template<class T>
class A<int, T*, 5> {}; // partial specialization where T1 is int, I is 5, and T2 is a pointer
 
template<class X, class T, int I>
class A<X, T*, I> {}; // partial specialization where T2 is a pointer

PTS的使用

首先,PTS是与主模板相伴而生的,也就是说,只要代码可以调用到主模板,那么PTS也自动的可以被调用到。例如:

namespace N {
    template<class T1, class T2> class A { }; // primary template
}
using N::A; // refers to the primary template
namespace N {
    template<class T> class A<T, T*> { }; // partial specialization
}
A<int,int*> a; // name lookup finds N::A (the primary template),
               // the partial specialization with T = int is then used

而如果没有主模板,PTS的存在就是不合法的。

接下来我们看一下如果一个主模板伴随定义了多个PTS,编译器如何在多个PTS当中进行选择。事实上,这个概念非常容易理解,因为它和编译器如何决定采用哪一个重载函数是一致的。

  1. 如果一个主模板下面定义了PTS,则如果在可能的条件下,主模板将是被调用的最后选择。由于PTS比主模板更加的详细规定了参数,所以可能的话,程序会优先调用PTS,而不是主模板。只有当所有的PTS都无法匹配模板参数的时候,才会调用主模板。
  2. 如果只有一个PTS可以匹配模板参数,那么就选用这个PTS。
  3. 如果有超过一个PTS可以匹配模板参数,则选择最细化的那个。
// given the template A as defined above

A<int, int, 1> a1;   // no specializations match, uses primary template
A<int, int*, 1> a2;  // uses partial specialization #1 (T=int, I=1)
A<int, char*, 5> a3; // uses partial specialization #3, (T=char)
A<int, char*, 1> a4; // uses partial specialization #4, (X=int, T=char, I=1)
A<int*, int*, 2> a5; // error: matches #2 (T=int, T2=int*, I=2)
                     //        matches #4 (X=int*, T=int, I=2)
                     // neither one is more specialized than the other

一开始的例子

现在我们可以理解本文一开始的例子了。当is的值为false的时候,我们获得的模板参数为<PT, false>。用这个参数列表来比对,发现只能使用主模板。而当is的值是true的时候,我们的道德模板参数为<PT, true>,存在比主模板更好的PTS,于是我们调用的便是PTS的版本。

Comments