一、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.