谓词与函数对象
谓词 predicate
C++ 标准定义谓词如下:
The Predicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing the corresponding iterator returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct if (pred(*first)){...}. The function object pred shall not apply any non-constant function through the dereferenced iterator. This function object may be a pointer to function, or an object of a type with an appropriate function call operator.
换成中文就是:当一个算法需要一函数对象,去应用于解引用(deferencing)迭代器(deferencing 解释: int p;将定义一个指向整数的指针,并p取消引用该指针,这意味着它将实际检索p指向的数据。)的结果,谓词参数返回一个testable值为true。换句话说,换句话说, 如果算法将谓词 pred 作为其参数,first
参数是迭代参数,则如果 (pred (* first)) {..}
, 它应该在构造中正常工作。函数对象 pred
不应通过解引用迭代器应用于任何非定常(non-constant)函数。此函数对象可以是指向函数的指针, 也可以是具有适当函数调用运算符的类型的对象。
仿函数
functor(函数对象或函数)形式:object
+ ()
这包括正常函数,函数指针和 () 运算符(函数调用运算符)重载的类对象,即为其定义函数 operator()() 类。
有时我们可以在普通函数不起作用时使用函数对象。STL经常使用函数对象并提供几个非常有用的函数对象。
函数对象是泛型编程的力量和纯抽象概念的另一个例子。我们可以说任何行为都像函数对象是函数。因此,如果我们定义一个行为是函数的对象,它可以用作函数。
#include <iostream> struct absValue { float operator()(float f){ return f> 0?f:-f; } }; int main() { using namespace std; float f = -123.45; absValue aObj; float abs_f = aObj(f); cout <<“f =”<< f <<“abs_f =”<< abs_f << '\n'; return 0; }
正如我们从定义中看到的那样,即使 aObj 是一个对象而不是一个函数,我们也可以对该对象进行调用。效果是运行由对象absValue定义的重载调用操作符。运算符获取浮点值并返回其绝对值。请注意,函数调用运算符必须声明为成员函数。
因此,定义调用操作符的类类型的对象(如absValue对象)称为函数对象。
一个函数的行为是可以通过使用括号和传递参数来调用。
func (arg1 , arg2);
如果我们希望对象以这种方式运行,我们必须通过使用括号和传递参数来调用它们。我们所要做的就是使用适当的参数类型定义operator() :
Class X { public: // define "function call" operator return-value operator() (arguments) const; ... };
然后我们可以使用这个类的对象来表现我们可以调用的函数:
X fn; //... fn(arg1,arg2); //为函数对象fn调用operator()
此调用相当于:
fn.operator()(ARG1,ARG2); //为函数对象fn调用operator()
一个例子:
class Print { public: void operator()(int elem)const { cout << elem <<“”; } }; int main(){ vector <int> vect; for(int i = 1; i <10; ++ i){ vect.push_back(i); } Print print_it; for_each(vect.begin(),vect.end(),print_it); cout << endl; return 0; }
for_each(vect.begin(),vect.end(),print_it);
通常,第三个参数可以是仿函数,而不仅仅是常规函数。实际上,这提出了一个问题。我们如何声明第三个参数?我们不能将它声明为函数指针,因为函数指针指定了参数类型。因为容器几乎可以包含任何类型,所以我们事先并不知道应该使用哪种特定类型。STL通过使用模板解决了这个问题。
template<class Iterator, class Function> Function for_each(Iterator first, Iterator last, Function f) { while (first != last) { f(*first); ++first; } return f; }
以下是Nicolai M. Josuttis在“The C ++ Standard Library”中列出的函数对象的一些优点。
-
功能对象是“智能功能”。
行为像指针的对象是智能指针。对于行为类似于函数的对象也是如此:它们可以是“智能函数”,因为它们可能具有超出operator()的能力。函数对象可以具有其他成员函数和属性。这意味着函数对象具有状态。 -
每个函数对象都有自己的类型。
普通函数只有在签名不同时才有不同的类型。但是,当函数对象的签名相同时,它们可以具有不同的类型。实际上,函数对象定义的每个函数行为都有自己的类型。这是使用模板进行泛型编程的重大改进,因为您可以将功能行为作为模板参数传递。 -
函数对象通常比普通函数更快。
模板的概念通常允许更好的优化,因为在编译时定义了更多细节。因此,传递函数对象而不是普通函数通常会产生更好的性能。
STL改进了仿函数概念,如下所示:
generator : 是一种可以不带参数调用的函数
一元函数:有一个参数的调用函数
二元函数:有两个参数的调用函数
generator 可以被认为是一种 每次调用都返回 集合/序列的下一个值的函数/对象。
算法的特殊辅助函数是谓词。谓词是返回布尔值的函数(或者可以隐式转换为bool的函数)。换句话说,谓词类是一个仿函数类,其operator() 函数是谓词,即其operator() 返回true或false。
谓词在STL中被广泛使用。标准关联容器的比较函数是谓词,谓词函数通常作为参数传递给find_if等算法。根据其目的,谓词是一元的或二元的。
一元函数返回一个布尔值是一元谓词。
二元函数返回一个布尔值是二元谓词。
谓词的种类算法(algorithm) 的 谓词(predicate) 详解:函数 函数指针 lambda表达式 函数对象 库定义的函数对象