C++函数对象-谓词

时间:2021-04-05 19:11:36

函数对象(也叫functor),听起来或许有点陌生,但他们是c++实体,即使你没有用过,也一定见过,只是你没有意识到而已。

从概念上讲,函数对象时用作函数的对象;但是从实现上来说,函数对象时实现了 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()之外,还可以拥有成员属性,下面对之前的一元函数稍作修改:

template<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算法中:

// A 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中很有用途