谓词函数、函数对象
从概念上讲,函数对象用作函数的对象;但是从实现上来说,函数对象时实现了 operate()的类的对象。
虽然函数和函数指针也可以归为函数对象,但实现了operate()的类的对象才能保存状态,才能用于STL。
我们直接看定义:
一元函数:接受一个参数的函数,如f(x)。
一元谓词函数:如果一元函数返回一个BOOL类型的值,则该函数称为谓词。
二元函数:接受2个参数的函数,如f(x,y)。
二元谓词函数:如果二元函数返回一个BOOL值,则该函数称为二元谓词。
之所以给返回布尔类型的函数对象专门命名,是因为谓词是用来为算法判断服务的。
一元函数:
下面给个很简单的一元函数的例子:
template<typename elementType> void FuncDispalyElement(const elementType& element) { cout<<element<<endl; }
该函数也可以采用另一种表现形式,即实现在包含在类或结构的operate()中:
template<typename T> struct DispalyElememnt { void opearator()(const T& elememnt) const { cout<<element<<endl; } };
上面两种实现都可以用于STL算法for_each,将集合中的类容显示在屏幕上。
#include <algorithm> #include <iostream> #include <vector> #include <list> using namespace std; // struct that behaves as a unary function template <typename elementType> struct DisplayElement { void operator () (const elementType& element) const { cout << element << ' '; } }; int main () { vector <int> vecIntegers; for (int nCount = 0; nCount < 10; ++ nCount) vecIntegers.push_back (nCount); list <char> listChars; for (char nChar = 'a'; nChar < 'k'; ++nChar) listChars.push_back (nChar); cout << "Displaying the vector of integers: " << endl; // Display the array of integers for_each ( vecIntegers.begin () // Start of range , vecIntegers.end () // End of range , DisplayElement <int> () ); // Unary function object cout << endl << endl; cout << "Displaying the list of characters: " << endl; // Display the list of characters for_each ( listChars.begin () // Start of range , listChars.end () // End of range , DisplayElement <char> () );// Unary function object return 0; }
其中for_each方法接受三个参数,前两个分别制定范围的起点和终点,第3个指定对范围类的每个元素调用的函数,如对vector调用DispalyElement::operate().
虽然这里2中方法都可以,但是结构体更加强大,因为它除了拥有operate()之外,还可以拥有成员属性,下面对之前的一元函数稍作修改:
te<typename elementType> struct DisplayElementKeepCount { int Count; // Constructor DisplayElementKeepCount() : Count(0) {} // Display the element, hold count! void operator()(const elementType& element) { ++ Count; cout << element<< ' '; } };
注意:operate()不再是const成员函数,因为它对成员Count进行递增,以记录自己被调用用于显示数据的次数,该计数是通过共有成员属性Count暴露的。下面是一个例子:
#include<algorithm> #include<iostream> #include<vector> using namespace std; template<typename elementType> struct DisplayElementKeepCount { int Count; // Constructor DisplayElementKeepCount() : Count(0) {} // Display the element, hold count! void operator()(const elementType& element) { ++ Count; cout << element<< ' '; } }; int main() { vector<int> vecIntegers; for(int nCount = 0; nCount< 10; ++ nCount) vecIntegers.push_back(nCount); cout << "Displaying the vector of integers: "<< endl; // Display the array of integers DisplayElementKeepCount<int> Result; Result = for_each( vecIntegers.begin() // Start of range , vecIntegers.end() // End of range // ,Result); //也可以用这行代替下一行 , DisplayElementKeepCount<int>() );// function object cout << endl<< endl; // Use the state stores in the return value of for_each! cout << "'"<< Result.Count<< "' elements were displayed!"<< endl; return 0; }
注意这次试用了for_each的返回值。
一元谓词
知道了一元函数,一元谓词也就很好理解了,下面我么给个例子,然后将一元谓词用于std::find_if算法中:
structure as a unary predicate template <typename numberType> struct IsMultiple { numberType Divisor; IsMultiple (const numberType& divisor) { Divisor = divisor; } bool operator () (const numberType& element) const { // Check if the dividend is a multiple of the divisor return ((element % Divisor) == 0); } }; #include <algorithm> #include <vector> #include <iostream> using namespace std; int main () { vector <int> vecIntegers; cout << "The vector contains the following sample values: "; // Insert sample values: 25 - 31 for (int nCount = 25; nCount < 32; ++ nCount) { vecIntegers.push_back (nCount); cout << nCount << ' '; } cout << endl << "Enter divisor (> 0): "; int Divisor = 2; cin >> Divisor; // Find the first element that is a multiple of 4 in the collection auto iElement = find_if ( vecIntegers.begin () , vecIntegers.end () , IsMultiple<int>(Divisor) ); if (iElement != vecIntegers.end ()) { cout << "First element in vector divisible by " << Divisor; cout << ": " << *iElement << endl; } return 0; }
二元函数与二元谓词
与一元函数一元谓词一模一样,只是参数变为2个,下面给出一个二元谓词对字符串vector排序的例子。
#include <algorithm> #include <string> using namespace std; class CompareStringNoCase { public: bool operator () (const string& str1, const string& str2) const { string str1LowerCase; // Assign space str1LowerCase.resize (str1.size ()); // Convert every character to the lower case transform (str1.begin (), str1.end (), str1LowerCase.begin (), tolower); string str2LowerCase; str2LowerCase.resize (str2.size ()); transform (str2.begin (), str2.end (), str2LowerCase.begin (), tolower); return (str1LowerCase < str2LowerCase); } }; #include <vector> #include <iostream> template <typename T> void DisplayContents (const T& Input) { for(auto iElement = Input.cbegin() // auto, cbegin and cend: c++11 ; iElement != Input.cend () ; ++ iElement ) cout << *iElement << endl; } int main () { // Define a vector of string to hold names vector <string> vecNames; // Insert some sample names in to the vector vecNames.push_back ("jim"); vecNames.push_back ("Jack"); vecNames.push_back ("Sam"); vecNames.push_back ("Anna"); cout << "The names in vector in order of insertion: " << endl; DisplayContents(vecNames); cout << "Names after sorting using default std::less<>: " << endl; sort(vecNames.begin(), vecNames.end()); DisplayContents(vecNames); cout << "Names after sorting using predicate that ignores case:" << endl; sort(vecNames.begin(), vecNames.end(), CompareStringNoCase()); DisplayContents(vecNames); return 0; }
小结:
一元谓词大量用于stl算法中,例如std::partition算法使用一元谓词来划分,stable_partition也使用一元谓词,还有诸如find_if()等查找,remove_if()等
删除元素的函数也使用一元谓词。对于二元谓词,同样也是,如删除响铃重复元素unique(),排序算法sort(),以及对2个范围进行操作的transform(),都需要二元谓词。谓词在stl中很有用途。