effective stl 第40条:若一个类是函数子,则应使他可配接

来源:互联网 发布:线性时间选择算法分析 编辑:程序博客网 时间:2024/06/05 21:08
#include<iostream>#include<list>#include<algorithm>#include<functional>using namespace std;bool isInsteresting(const string* obj)//判断某个类型是否有趣{    if ((*obj)[4] % 7 == 0)    {        return true;    }    return false;}int main(){    list<string*> listString(20);    for (int i = 0; i < 26; i++)    {        listString.push_back(new string("haha"+(i+'a')));    }    //接下来找到第一个满足isInsteresting的string    list<string*>::iterator i = find_if(listString.begin(), listString.end(), isInsteresting);    if (i != listString.end())    {        //找到满足的条件之后    }    //反之,如果想找到第一个不满足isInterseting条件的string指针,下边的方法显而易见却不能通过编译    //list<string*>::iterator iN = find_if(listString.begin(), listString.end(), not1(isInsteresting));    //正确的方法是,在应用Not1之前,必须加上ptr_fun    list<string*>::iterator iN = find_if(listString.begin(), listString.end(), not1(ptr_fun(isInsteresting)));    if (i != listString.end())    {        //doSomething    }    return 0;}

为什么加上ptr_fun就能编译运行了呢,问题的答案也许有些出乎意料,ptr_fun只不过完成了一些类型定义的工作,而这些类型定义是Not1所必须的。isInteresting作为一个基本的函数指针,它缺少Not1所需要的类型。

4个标准的函数配接器(not1、not2/bind1st/bind2nd(注:std::bind1st 和 std::bind2nd将二元函数转换为一元函数bind1st和bind2的使用方法详解))都有一些特殊的类型定义,那些非标准的、与STL兼容的配接器通常也是如此。提供了这些必要的类型定义的函数对象称为可配接的函数对象,反之,如果函数对象缺少这些类型对象,则称为不可配接的。

特殊的类型定义是指: argument_type、fist_argument_type、second_argument_type、result_type
除非你要编写自定义的配接器,否则你不需要知道有关这些类型定义的细节。
如果函数子类的operator()只有一个实参,那么它应该从std::unary_function继承,如果函数子类的operator()有两个实参,那么它应该从std::binary_function继承。

由于unary_function和binary_function是STL的模板,所以不能直接继承,相反,必须继承它们所产生的结构,这就要求你指定某些类型实参。对于unary_function,必须指定函数子类operator的参数类型,以及返回类型;对于Binary_function,必须指定三个类型:operator的第一个参数、operator的第二个参数和operator的返回类型。

eg:

template<typename T> class MeetsThreshold : public unary_function<string, bool>{private:    const T threshold;public:    MeetsThreshold(const T& threshold);    bool operator()(const string&)const;};struct WidgeNameCompare :    public binary_function<string, string, bool>{    bool operator()(const string& lhs,const string& rhs) const;};

注意:传递给unary_function和binary_function的模板参数这是函数子类的operator()的参数类型和返回类型,唯一可能有点奇怪的是,operaror()的返回类型是unary_function和binary_function的最后一个参数。

你可能注意到了MeetsThreshold是一个类,而WidgetNameCompare是一个结构。MeetsThreshold有内部状态(它的阈值数据成员),而类是封装那些信息的合理方法。WidgetNameCompare没有状态,因此不需要任何private的东西。所有东西都是public的仿函数类的作者经常把它们声明为struct而不是class,也许只因为可以避免在基类和operator()函数前面输入“public”。把这样的仿函数声明为class还是struct纯粹是一个个人风格问题。如果你仍然在精炼你的个人风格,想找一些仿效的对象,看看无状态STL自己的仿函数类(比如,less、plus等)一般写为struct。

再来看WidgetNameCompare虽然operator()的参数类型是const string&,但是传递给binary_function的类确实WIdget。一般情况下,传递给unary_function和binary_function的非指针类型需要去掉const和引用&。如果operator()带有指针参数,则规则就不同了,下边是widgetNameCompare含数字的另一个不同的版本

struct WidgeNameCompare :    public binary_function<const string*, const string*, bool>{    bool operator()(const string*, const string*) const;};

对于以指针为参数或者返回诶性的函数子类,一般的规则是,传给unary_function或者binary_function的类型与opertor()参数和类型完全相同。

//找到最后一个不符合阈值"haha"的对象    list<string*> str1;    list<string*>::reverse_iterator ri = find_if(str1.rbegin(), str1.rend(), not1(MeetsThreshold<string>("haha")));    //找到按照WidgetNameCompare的排序规则,排在s之前的第一个string对象    string s("haha");    list<string*>::iterator ri = find_if(str1.begin(), str1.end(), bind2nd(WidgeNameCompare(), s));

STL是假设每一个函数子类只有一个operator()成员函数,
当然也可以将两个operator()何在一起,但是最好不要这样做,因为这样的函数子 只有一半的配接功能,只有一半配接功能的函数子并不比完全不配接的函数子强多少。

0 0
原创粉丝点击