C++标准库扩展,查找元素,使用修饰者模式构造查询条件


C++标准库的的查找算法std::find_if 允许提供一个pred函数用于在容器内查找满足pred函数调用为真的元素。

同样以学生信息结构为例,常常会需要查找结构的某个成员是特定值的元素,比如查找所有计算机系的学生。

每次为这种特定需求写一个函数对象不够灵活,我们也希望通过一组通用的函数对象构造模板来实现。


下面这个函数对象用于比较对象的某个成员是否和输入的值相等:


template<class TField, class TValue>
struct FieldEqualPred
{
    TField f_;     //参与比较的数据成员指针
    TValue v_;     //比较的值

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return l.*f_ == v_;
    }

    //构造函数
    FieldEqualPred(TField f, const TValue &v) : f_(f),v_(v){}
};



对应上面函数对象的生成函数:

template<class F, class V>
FieldEqualPred<F,V> equal (F f, const V &v){
    return FieldEqualPred<F,V>(f,v);
}



另外一组提供不等于的条件:

template<class TField, class TValue>
struct FieldNoEqualPred
{
    TField f_;     //参与比较的数据成员指针
    TValue v_;     //比较的值

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return l.*f_ != v_;
    }

    //构造函数
    FieldNoEqualPred(TField f, const TValue &v) : f_(f),v_(v){}
};

template<class F, class V>
FieldNoEqualPred<F,V> noequal(F f, const V &v){
    return FieldNoEqualPred<F,V>(f,v);
}


接下来就可以工作了:

int main()
{
    std::vector<Person> persons;
    
    persons.push_back(Person("=xiaowang", "class3", "computer"));
    persons.push_back(Person("=xiaoli", "class1", "math"));
    persons.push_back(Person("=xiaozhu", "class2", "computer"));

    //查找年级是class1的学生
    std::find_if(persons.begin(), persons.end(), equal(&Person::strClass, std::string("class1")));

    return 0;
}


但如果要支持更复杂的判断逻辑,比如and和or连接多个成员的比较函数呢

好像也不难,写出下面的函数对象定义:

template<class Pred1, class Pred2>
struct LogicAndPred
{
    Pred1 p1_;     //左边的比较对象
    Pred2 p2_;     //右边的比较对象

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return p1_(l) && p2_(l);
    }

    //构造函数
    LogicAndPred(Pred1 &p1, Pred2 &p2) : p1_(p1),p2_(p2){}
};

template<class Pred1, class Pred2>
struct LogicOrPred
{
    Pred1 p1_;     //左边的比较对象
    Pred2 p2_;     //右边的比较对象

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return p1_(l) || p2_(l);
    }

    //构造函数
    LogicOrPred(Pred1 &p1, Pred2 &p2) : p1_(p1),p2_(p2){}
};

template<class P1, class P2>
LogicAndPred<P1,P2> and (P1 &p1, P2 &p2){
    return LogicAndPred<P1,P2>(p1,p2);
}


template<class P1, class P2>
LogicOrPred<P1,P2>  or (P1 &p1, P2 &p2){
    return LogicOrPred<P1,P2>(p1,p2);
}

int main()
{
    std::vector<Person> persons;
    
    persons.push_back(Person("=xiaowang", "class3", "computer"));
    persons.push_back(Person("=xiaoli", "class1", "math"));
    persons.push_back(Person("=xiaozhu", "class2", "computer"));

    //查找数学系且年级是class1的学生
    std::find_if(persons.begin(), persons.end(), and(equal(&Person::strDepart, std::string("math")), equal(&Person::strClass, std::string("class1"))));
    return 0;
}


感觉良好,如果再加一个条件呢,是不是就有点难以理解了。。。


更好的形式可能是这样的:

    equal(&Person::strDepart, std::string("math"))
    .and(equal(&Person::strClass, std::string("class1"))
              .or_equal(&Person::strClass, std::string("class2")));

上面语句构造的条件查找的是数学系的,且班级是class1或者class2的学生。


分析上面的语句,看来生成函数equal或noequal返回的不能是简单的函数对象了,此函数对象还需要包含如下一些方法:

and

or

and_equal

or_equal

and_noequal

or_noequal

。。。


为每个函数对象都定义这么多方法吗,好像不是很容易的事,更简单的做法是使用修饰者模式写一个修饰类:

template<class TField, class TValue, template<class T> class TPred>
struct FieldPred
{
    TField f_;     //参与比较的数据成员指针
    TValue v_;     //比较的值

    //构造函数
    FieldPred(TField f, const TValue &v) : f_(f),v_(v){}

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return compare(l.*f_);
    }

    template<class T>
    bool compare(T &left)
    {
        return TPred<T>()(left, v_);
    }
};

template<class Pred1, class Pred2, template<class T> class TPred>
struct LogicPred
{
    Pred1 p1_;     //左边的比较对象
    Pred2 p2_;     //右边的比较对象

    //构造函数
    LogicPred(Pred1 &p1, Pred2 &p2) : p1_(p1),p2_(p2){}

    //比较函数
    template<class T>
    bool operator ()(const T& l)
    {
        return TPred<bool>()(p1_(l), p2_(l));
    }
};

template<class TInner>
struct LogicLink : TInner
{
    LogicLink(TInner inner) : TInner(inner){}

    template<class F, class V>
    LogicLink<LogicPred<TInner, FieldPred<F,V,std::equal_to>, std::logical_and> >
    and_equal(F f, const V &v)
    {
        typedef LogicPred<TInner, FieldPred<F,V,std::equal_to>, std::logical_and> type;
        return type((TInner&)(*this), FieldPred<F,V,std::equal_to>(f,v));
    }
    template<class P>
    LogicLink<LogicPred<TInner, P, std::logical_and> > and(P &p)
    {
        return LogicPred<TInner, P, std::logical_and>((TInner&)(*this), p);
    }
};


对应的,生成函数改造如下:

template<class F, class V>
LogicLink<FieldPred<F,V,std::equal_to> >equal(F f, const V &v){
    return FieldPred<F,V,std::equal_to>(f,v);
}

template<class F, class V>
LogicLink<FieldPred<F,V,std::not_equal_to> > noequal(F f, const V &v){
    return FieldPred<F,V,std::not_equal_to>(f,v);
}



上面的生成函数使用LogicLink来修饰了原有的函数对象,达到在函数对象上增加and,or等连接操作的能力

剩下其它的几个方法感兴趣的同学可以实现下^^











评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值