在學習到《Essential C++》3.6節時自己按照自己的想法來實現書上提到的思路。代碼雖然簡單,不過自己卻發現有很多其他地方不熟悉,比如函數指標的用法,仿函數和函數適配器的使用。搗鼓了半天,總算懂了點眉目,簡單總結下以備忘。
文檔按照五部分分寫,先依次簡單地介紹find()與find_if()演算法、提及函數作為參數的用法(我犯錯了,這裡。)、仿函數(functon object)和函數適配器(function adapters)。最後貼上自己實驗的代碼並簡單分析指出錯誤。
一、
因為問題起於泛型搜尋演算法find_if(),所以先總結下有關find()和find_if()。
Code:
- 聲明:
- InputIterator
- find (InputIterator beg, InputIterator end, const T& value)
-
- InputIterator
- find_if(InputIterator beg, InputIterator end, UnaryPredicate op)
第一種形式返回在搜尋域[beg,end)內與value值相等的元素的地址;
第二種形式返回在搜尋域[beg,end)內滿足一元謂詞函數op(elem)為真的元素的地址。
? 1)以上兩種形式在搜尋不到匹配元素的情況下均返回end;
? 2)注意op函數調用期間不能夠改變自身的狀態;
? 3)op不能更改傳遞進來的參數;
二、
為了增強泛型演算法的靈活性,一些泛型演算法允許傳遞使用者自定的的函數。泛型演算法在使用輔助函數時有不同的方式,有些是可以省略的,即演算法是可以不用給定的函數,而有些是強制性的,即必須使用這些函數。如下
Code:
- // CODE SNIPPET
- void print (int elem)
- {
- cout <<elem <<’ ‘;
- }
- ……
- for_each (coll.begin(), coll.end(), print);
注意如上for_each語句中的第三個參數,這個參數接受一個函數指標,此時傳遞的便是函數print()的地址(和數組類似,其名也可指示其地址)。注意,此時不要用print(elem).
一種特殊的輔助函數便是謂詞函數,謂詞函數通常指的是傳回值為boolean的函數,它們通常用來為排序或者搜尋演算法指定參照條件。在使用中謂詞函數可分為帶有一個參數的謂詞函數(unary)和需要兩個參數的謂詞函數(binary),值得注意的是並不是所有傳回值為boolean類型的函數都是合法的謂詞函數。
三、
仿函數是一種對象,但它具有函數一樣的功能,並且比函數更有優勢。其中一點便是效率更高。我們使用的仿函數通常被聲明為inline,所以其大部分執行邏輯在編譯時間已經基本確定,因而少掉了頻繁調用函數的開銷。
可以自己定義簡單的仿函數,如下:
Code:
- // CODE SNIPPET
- class Print{
- public:
- void operator() (int elem) const
- {
- cout <<elem <<’ ‘;
- }
- };
- ……
- for_each (coll.begin(), coll.end(), Print());
- ……
C++的STL中提供了多種預定義的仿函數,如negate<type>(), plus<type>(), minus<type>(), multiplies<type>(), divides<type>(), modulus<type>(), equal_to<type>(),not_equal_to<type>(), less<type>(), greater<type>(), less_equal<type>(), greater_equal<type>(), logical_not<type>(), logical_and<type>(), logical_or<type>().
四、
函數適配器也是一種仿函數,不過這種仿函數更為高等,因為它可以將一個仿函數與另一個仿函數/ 值/ 特殊的函數結合起來。
不難發現,STL常使用仿函數雖然提供了多種有用的仿函數,然而這些仿函數均是通過迭代器對容器裡面的元素進行操作,但是有時候你還需要用其他的值結合,這時候便可以使用函數適配器(見下面的例子)。
編譯不能通過的代碼如下所示:
Code:
- // code snippet
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include <functional>
-
- using namespace std;
-
- bool less_than(int a, int b)
- {
- return (a<b ? true:false);
- }
-
- vector<int> filter(const vector<int> &vec, int filter_value, bool (*ptr_func)(int, int))
- {
- vector<int> temp;
- vector<int>::const_iterator iter = vec.begin();
-
- while (find_if(iter, vec.end(), ptr_func(*iter, filter_value)) != vec.end())
- {
- temp.push_back(*iter);
- ++iter;
- }
- return temp;
- }
-
- void display(vector<int> &vec)
- {
- for (int ix=0; ix<vec.size(); ++ix)
- cout <<vec[ix] <<" ";
- cout <<"/n";
- }
-
- int main()
- {
- const int nsize = 5;
- int int_arr[nsize] = { 2, 4, 5, 6, 7};
- vector<int>vec(int_arr, int_arr+nsize);
- display(vec);
- vector<int>result;
-
- result = filter(vec, 6, less_than);
- display(result);
-
- return 0;
- }
可見,我在使用find_if()函數的時候,將裡面本應該為“函數指標”的第三個參數寫成了對這個函數的“函數調用”,這是不允許的。但整個程式的思路並沒有錯,因為上面的代碼可以通過仿函數來解決的。
上面也提到,一般通用的仿函數僅是對容器裡面的值進行處理,但本例的思路是需要將容器裡面的每個值以此與另一個值val進行比較的。所以必須要使用到函數適配器來將一個仿函數與一個值進行綁定。如下是更正後的代碼:
Code:
- // code snippet
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include <functional>
-
- using namespace std;
-
- bool less_than(int a, int b)
- {
- return (a<b ? true:false);
- }
-
- vector<int> filter(const vector<int> &vec, int val, less<int><)
- {
- vector<int> nvec;
- vector<int>::const_iterator iter = vec.begin();
-
- while ((iter=find_if(iter, vec.end(), bind2nd(lt, val))) != vec.end())
- {
- nvec.push_back(*iter);
- iter ++;
- }
- return nvec;
- }
-
- void display(vector<int> &vec)
- {
- for (int ix=0; ix<vec.size(); ++ix)
- cout <<vec[ix] <<" ";
- cout <<"/n";
- }
-
- int main()
- {
- const int nsize = 5;
- int int_arr[nsize] = { 2, 4, 5, 6, 7};
- vector<int>vec(int_arr, int_arr+nsize);
- display(vec);
- vector<int>result;
-
- result = filter(vec, 6, less<int>());
- display(result);
-
- return 0;
- }