您的位置:首页 > 编程语言 > C语言/C++

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

2013-04-24 00:22 337 查看
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等连接操作的能力

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

4000
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐