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等连接操作的能力
剩下其它的几个方法感兴趣的同学可以实现下^^