一樣,函數指針型模板參數的意義在於:在變與不變之間取得最優實現。一般函數指針的做用是實現回調(callback),即由調用方將所須要操 做包裝成某個函數f0,並將指向此函數的指針&f0做爲參數傳遞給函數f1。函數f1在運行時回調所指函數f0,從而實現調用方所期待操做。f0 就是一個回調函數。將回調函數指針做爲被調用函數的參數時,實現的是動態回調,只有在運行時才能肯定是回調哪一個函數。若是並不須要動態回調,只爲方便,在 已有算法框架中嵌入所需操做,則動態回調就略顯牛刀殺雞。此時,將回調函數指針從函數參數移到模板參數,就很容易實現「靜態回調」。ios
注意 之因此在「靜態回調」上加引號,是由於其並不能算作嚴格的「回調」。回調函數指針是在編譯時給定,在模板實例中,就至關於將本來用指針進行回調的地方用所給函數代替,成爲徹底普通的函數調用。這樣也節省了一點點讀取指針值肯定函數入口的額外運行開銷。算法
例3.1雖然看起來有些古怪,卻演示了函數指針型模板參數的用法。假設對某一數組,須要先依次打印數組元素,依次將元素加1,依次打印,依次將元素減1,再依次打印。數組
之因此強調「依次」,是由於對於數組及其餘任何序列來講,「依次」操做是一種最多見的需求。但很遺憾,不管C或者C++,都未將「依次」做爲內建支 持。每次都用for循環語句來遍歷數組當然可行,卻會使代碼看起來冗長。更好的作法是將「依次」抽象成可複用代碼,好比一個foreach函數,再將對數 組元素所作操做包裝成一個回調函數,並將其指針傳入foreach函數便可。當操做有限且已知時,顯然應該將函數指針從foreach函數參數移至模板參 數實現「靜態回調」。瞭解用意後,就不難理解例3.1的代碼了。框架
例3.1
#include <iostream>函數
// 定義一個foreach函數模板,對數組的每個元素進行某種操做
// 具體操做由模板的函數指針參數指定
template<typename T, void (*f)(T &v)>
void foreach(T array[], unsigned size)
{
for (unsigned i = 0; i < size; ++i) f(array[i]);
}spa
// 三個函數模板用來定義對數組元素的操做
template<typename T>
void inc(T &v) {++v;}指針
template<typename T>
void dec(T &v) {--v;}回調函數
template<typename T>
void print(T &v) {std::cout << ' ' << v;}io
int main()
{
int array[] = {1, 2, 3, 4, 5, 6, 7, 8};for循環
using namespace std;
foreach<int, print<int> >(array, 8);
cout << endl;
foreach<int, inc<int> >(array, 8);
foreach<int, print<int> >(array, 8);
cout << endl;
foreach<int, dec<int> >(array, 8);
foreach<int, print<int> >(array, 8);
cout << endl;
return 0;
}
例3.1中,foreach定義爲一個函數模板,其第二個模板參數是一個函數指針。因爲模板參數必須在編譯時給定,因此這個foreach函數實現 了「靜態回調」。另有一個值得注意的是,第二個模板參數void(*f)(T*v)的定義中用到第一個模板參數T。這是合法的,在模板參數列表中,所聲明 模板參數可當即用於定義隨後的模板參數。在本例中,void (*f)(T*v)約束指針型模板參數f所指函數只能接受與前一模板參數類型相同的指針參數,隨後所定義的三個函數模板都是要對數據元素進行的操做,分別 是對元素值加一、減1以及打印。在main函數中則調用foreach函數模板,靜態綁定對數組每一個元素分別加一、減1以及打印。