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

#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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值