Step By Step(C++模板Policy)


一、Policy類:

    該篇博客中的代碼示例將承接上一篇博客(C++模板Trait)中給出的累加器的例子。在之前的代碼中,我們都是讓累加器完成固定的累加行為,即累加求和。然而事實上,我們仍然可以修改該函數的累加算法,比如將求和算法改為求積算法。或者說,如果參數類型是字符的話,我們也可以通過該函數進行指定形式的字符連接。在實際的代碼修改中,我們只需將total += *begin代碼行替換為我們為該函數指定的Policy模板參數,這樣我們就將該模板參數稱為該函數累加過程的一個Policy。見如下代碼示例和關鍵性注釋:

 1     #include <stdio.h>
 2     
 3     template<typename T>
 4     class AccumulationTraits;
 5     
 6     template<>
 7     class AccumulationTraits<char> {
 8     public:
 9         typedef int AccT;
10         static AccT zero() { return 0; }
11     };
12     
13     template<>
14     class AccumulationTraits<short> {
15     public:
16         typedef int AccT;
17         static AccT zero() { return 0; }
18     };
19     
20     template<>
21     class AccumulationTraits<int> {
22     public:
23         typedef long long AccT;
24         static AccT zero() { return 0; }
25     };
26     
27     template<>
28     class AccumulationTraits<unsigned int> {
29     public:
30         typedef unsigned long long AccT;
31         static AccT zero() { return 0; }
32     };
33     
34     template<>
35     class AccumulationTraits<float> {
36     public:
37         typedef double AccT;
38         static AccT zero() { return 0; }
39     };
40     
41     class SumPolicy {
42     public:
43         template<typename T1,typename T2>
44         static void accumulate(T1& total, T2 const& value) {
45             total += value;
46         }
47     };
48     
49     class MultiPolicy {
50     public:
51         template<typename T1,typename T2>
52         static void accumulate(T1& total, T2 const& value) {
53             total *= value;
54         }
55     };
56     
57     template<typename T, typename Policy = SumPolicy, typename Traits = AccumulationTraits<T> >
58     class Accum {
59     public:
60         typedef typename Traits::AccT AccT;
61         static AccT accumulate(T const* begin, T const* end) {
62             AccT total = Traits::zero();
63             while (begin != end) {
64                 Policy::accumulate(total,*begin);
65                 ++begin;
66             }
67             return total;
68         }
69     };
70     
71     int main() {
72         int test[5] = {1,2,3,4,5};
73         int r = Accum<int,MultiPolicy>::accumulate(test,test + 5);
74         printf("r is %d.",r);
75         return 0;
76     }
77     //r is 0.

    這里之所以結果為0,是因為Traits類AccumulationTraits<int>::zero()方法返回的值是0。通過這個示例,可以讓我們清楚的認識到,是組合使用Traits和Policy的時候,應該講更多的細節問題考慮進來。
    
二、Traits和Policy的主要差別:

    以下為Trait的特征和適用場景:
    1. Trait表述了模板參數的一些自然的額外屬性。
    2. Trait可以是固定Trait,也就是說, 不需要通過模板參數進行傳遞。
    3. Trait參數通常都具有很自然的缺省值,該缺省值很少會被改寫,或者說是根本就不能被改寫。
    4. Trait參數可以緊密依賴於一個或多個主參數。
    5. Trait通常都是用Trait模板來實現的。
    
    下面是Policy的特征和應用場景:
    1. Policy表述了泛型函數和泛型類的一些可配置行為。
    2. 如果不以模板參數的形式進行傳遞的話,Policy Class幾乎不起作用。
    3. Policy參數並不需要具有缺省值,而且通常都是顯示指定這個參數,盡管許多泛型組件都配置了使用頻率很高的缺省Policy。
    4. Policy參數和屬於同一個模板的其他參數通常都是正交的。
    5. Policy class一般都包含了成員函數。
    6. Policy即可以用普通類來實現,也可以用模板類來實現。
    
三、用模板的模板參數來實現Policy Class:

    上面的示例只是使用普通類的模板成員函數來表示Policy的,而這里我們使用模板類來重新設計這個Policy class。見如下代碼片段和關鍵性注釋:

 1     template<typename T1,typename T2>
 2     class SumPolicy {
 3     public:
 4         static void accumulate(T1& total, T2 const& value) {
 5             total += value;
 6         }
 7     };
 8     
 9     template<typename T1,typename T2>
10     class MultiPolicy {
11     public:
12         static void accumulate(T1& total, T2 const& value) {
13             total *= value;
14         }
15     };
16     //這里已經將第二個模板參數定義為模板類類型,其中該模板類本身要有兩個模板參數,即返回值類型和元素類型。
17     template<typename T, 
18              template<typename,typename> class Policy = SumPolicy, 
19              typename Traits = AccumulationTraits<T> >
20     class Accum {
21     public:
22         typedef typename Traits::AccT AccT;
23         static AccT accumulate(T const* begin, T const* end) {
24             AccT total = Traits::zero();
25             while (begin != end) {
26                 Policy<AccT,T>::accumulate(total,*begin);
27                 ++begin;
28             }
29             return total;
30         }
31     };
32     
33     int main() {
34         int test[5] = {1,2,3,4,5};        
35         int r = Accum<int,MultiPolicy>::accumulate(test,test + 5);
36         printf("r is %d.",r);
37         return 0;
38     }
39     //r is 0.

四、類型函數:

    對於普通函數而言,我們可以將其稱為值函數,既函數接收的是某些值,同時返回結果也是值。但是對於類型函數則有些不同,他們一般接收的參數是類型實參,同時也會生成一個類型或常量作為返回的結果。下面是一個簡單的類型函數的示例,主要行為是通過類型函數實現sizeof的功能。

 1     #include <stdio.h>
 2     #include <iostream>
 3     
 4     using namespace std;
 5     
 6     template<typename T>
 7     class TypeSize {
 8     public:
 9         static size_t const value = sizeof(T);
10     };
11     
12     int main() {
13         cout << "TypeSize<int>::value = " << TypeSize<int>::value << endl;
14         return 0;
15     }
16     //TypeSize<int>::value = 4

    從上例可以看出,所謂的類型函數並不局限於函數本身,也可以用模板類來實現。下面將給出一個更為實用的應用場景。比如,我們的函數參數是一個容器類型,該類型為模板參數,而該函數的功能是遍歷容器並返回所有元素的累加之和,該返回值的類型則需要視容器元素的類型而定,這里我們先給出一個普通的實現方式,如:

1     template<typename ElementT, typename ContainerT>
2     ElementT sumOfElements(ContainerT const& c) {
3         ElementT total = ElementT();
4         ContainerT::const_iterator it = c.begin();
5         for (; it != c.end(); ++it)
6             total += *it;
7         return total;            
8     }

    在上面的例子中,我們聲明的函數必須同時提供兩個模板參數,既容器類型和容器元素類型的模板參數。通過類型函數,我們可以只為該函數聲明一個模板參數便可,既只有容器類型的類型參數。見如下代碼和關鍵性注釋:

 1     #include <stdio.h>
 2     #include <vector>
 3     #include <list>
 4     #include <stack>
 5     #include <typeinfo>
 6     #include <iostream>
 7     #include <conio.h>
 8     
 9     using namespace std;
10     
11     template<typename T>
12     class ElementT;       //基本模板,缺省情況下不需要提供定義。
13     
14     template<typename T>
15     class ElementT<vector<T> > {  //基於vector的特化
16     public:
17         typedef T Type;
18     };
19     
20     template<typename T>
21     class ElementT<list<T> > {    //基於list的特化
22     public:
23         typedef T Type;
24     };
25     
26     template<typename T>
27     class ElementT<stack<T> > {   //基於stack的特化
28     public:
29         typedef T Type;
30     };
31     
32     template<typename T>
33     void printElementType(T const& c) { //一個普通的測試方法,用於測試上面的類型函數。
34         cout << "Container of " << typeid(typename ElementT<T>::Type).name() 
35              << " elements.\n";
36     }
37     //在這里我們可以看到,sumOfElement只有一個模板參數了,而另一個類型參數,既元素的
38     //的類型,我們已經通過類型函數獲取到了。    
39     template<typename C>
40     typename ElementT<C>::Type sumOfElement(C const& c) {
41         typedef typename ElementT<C>::Type Type;
42         Type total = Type();
43         C::const_iterator it = c.begin();
44         for (; it != c.end(); ++it)
45             total += *it;
46         return total;
47     }
48     
49     int main() {
50         stack<bool> s;
51         printElementType(s);
52         vector<int> v;
53         v.push_back(1);
54         v.push_back(3);
55         v.push_back(5);
56         v.push_back(7);
57         v.push_back(9);
58         int r = sumOfElement(v);
59         printf("The result of sumOfElement is %d.",r);
60         getch();
61         return 0;
62     }
63     //Container of bool elements.
64     //The result of sumOfElement is 25.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM