编写很多小的函数对象的缺点:
1, 大量的小类分散在代码中,这样很难进行维护
2,理解函数对象被调用处的代码会很难Boost.Lambda :
1】可以创建直接定义和调用的函数对象,或者把它保存起来晚一些再调用;
2】增加了控制结构(使用嵌套的循环结构,把算法表达式写入循环中)、表达式到函数对象的自动转换,还支持在 lambda 表达式中的异常处理
Lambda库优点:
将任意的表达式转换为可以兼容标准库算法的函数对象
就地定义匿名函数,提高代码的可读性和可维护性
避免代码膨胀和功能的分散。
std::cout <<*std::find_if(vec1.begin(),vec1.end(),
(_1>=3&& _1<5) || _1<1) << '\n';
std::cout <<*std::find_if(vec2.begin(),vec2.end(),
_1>=4&& _1<10) << '\n';
std::cout <<*std::find_if(vec1.begin(),vec1.end(),
_1==4 || _1==5)<< '\n';
std::cout <<*std::find_if(vec2.begin(),vec2.end(),
_1!=7 &&_1<10) << '\n';
std::cout <<*std::find_if(vec1.begin(),vec1.end(),
!(_1%3)) <<'\n';
std::cout <<*std::find_if(vec2.begin(),vec2.end(),
_1/2<3)<< '\n';
例子:简洁、可读、可维护,这就是使用 Boost.Lambda 所得到的代码的风格。
std::for_each(vec.begin(),vec.end(),_1+=10);
std::for_each(vec.begin(),vec.end(),_1-=10);
std::for_each(vec.begin(),vec.end(),_1*=3);
std::for_each(vec.begin(),vec.end(),_1/=2);
std::for_each(vec.begin(),vec.end(),_1%=3);代替ptr_fun 和 mem_fun:
void plain_function(int i) { 函数
std::cout << "voidplain_function(" << i << ")\n";
}
class some_class {
public:
void member_function(int i) const { 类的成员函数
std::cout <<
"void some_class::member_function("<< i << ") const\n";
}
};
std::vector<int> vec(3);
vec[0]=12;
vec[1]=10;
vec[2]=7;
some_class sc;
some_class* psc=≻
以前:
std::for_each(
vec.begin(),
vec.end(),
std::ptr_fun(plain_function));//ptr_fun
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun_ref(&some_class::member_function),sc));
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun(&some_class::member_function),psc));
现在:均使用bind
std::for_each(
vec.begin(),
vec.end(),
bind(&plain_function,_1));
std::for_each(vec.begin(),vec.end(),
bind(&some_class::member_function,sc,_1));
std::for_each(vec.begin(),vec.end(),
bind(&some_class::member_function,psc,_1));
编写可读的谓词优点:
因为代码可以更容易理解(所有功能都在同一个地方),也是因为代码不会被一些极少使用的函数对象搞混。
如果该元素类型已经定义了operator==,则可以直接使用算法find。
如何使用find来查找第一个元素,其满足成员函数a返回"apple"的条件?
class search_for_me {
std::string a_;
std::string b_;
public:
search_for_me() {}
search_for_me(const std::string&a,const std::string& b)
: a_(a),b_(b) {}
std::string a() const {
return a_;
}
std::string b() const {
return b_;
}
};
std::vector<search_for_me> vec;
vec.push_back(search_for_me("apple","banana"));
vec.push_back(search_for_me("orange","mango"));
std::vector<search_for_me>::iteratorit=
std::find_if(vec.begin(),vec.end(),???);
if (it!=vec.end())
std::cout << it->a() <<'\n';
以前:
class a_finder {
std::string val_;
public:
a_finder() {}
a_finder(const std::string& val) :val_(val) {}
bool operator()(const search_for_me& s)const {
return s.a()==val_;
}
};
这个函数对象可以这样使用:
std::vector<search_for_me>::iteratorit=
std::find_if(vec.begin(),vec.end(),a_finder("apple"));
缺点:
但两分钟(或几天)后,我们想要另一个函数对象,这次要测试成员函数
b 等等-------重复的工作
现在:
std::vector<search_for_me>::iteratorit=
std::find_if(vec.begin(),vec.end(),
bind(&search_for_me::a,_1)=="apple");函数对象可以与 Boost.Lambda 一起使用
需要使用bind<返回值类似>或ret<返回值类型>明确指定返回值得类型
或者函数类型中使用sig泛型类。
template<typename T> class add_prev {
T prev_;
public:
T operator()(T t){
prev_+=t;
return prev_;
}
};
using namespaceboost::lambda;
std::vector<int>vec;
vec.push_back(5);
vec.push_back(8);
vec.push_back(2);
vec.push_back(1);
add_prev<int>ap;
std::transform(
vec.begin(),
vec.end(),
vec.begin(),
bind(var(ap),_1));
错误编译:当绑定器被实例化时,返回类型推断的机制被使用…而且失败了。
必须这样写:
std::transform(vec.begin(),vec.end(),vec.begin(),
bind<int>(var(ap),_1));
它等价于这段代码:
std::transform(vec.begin(),vec.end(),vec.begin(),
ret<int>(bind(var(ap),_1)));
使用sig改进add_prev的版本:不再需要在lambda表达式中使用返回类型
public std::unary_function<T,T> {
T prev_;
public:
template <typename Args> class sig{
public:
typedef T type;
};
因此我们最早那个版本的代码现在可以编译了。
std::transform(vec.begin(),vec.end(),vec.begin(),bind(var(ap),_1));
注:
模板参数Args
实际上是一个 tuple,包含了函数对象(第一个元素)和调用操作符的参数类型
例子:
来看另一个有两个调用操作符的函数对象,其中一个版本接受一个int参数,
另一个版本接受一个conststd::string引用。
必须要解决的问题:
如果传递给sig模板的 tuple 的第二个元素类型为int, 则设置返回类型为std::string;
如果传递给sig模板的 tuple 的第二个元素类型为std::string,则设置返回类型为double"。
方法:对类模板偏特化
template <typename T> classsig_helper {};
int偏特化
template<> classsig_helper<int> {
public:
typedef std::string type;
};
string偏特化
template<> classsig_helper<std::string> {
public:
typedef double type;
};
函数对象
// The function object
class some_function_object {
template <typename Args> class sig {
typedef typename boost::tuples::element<1,Args>::type
cv_first_argument_type;
typedef typename
boost::remove_cv<cv_first_argument_type>::type
first_argument_type;
public:
// The first argument helps us decide thecorrect version
typedef typename
sig_helper<first_argument_type>::typetype;
};
std::string operator()(int i) const {
std::cout << i << '\n';
return "Hello!";
}
double operator()(const std::string& s)const {
std::cout << s << '\n';
return 3.14159265353;
}
};
要注意的部分是sig类,它的第一个参数(即 tuple 的第二个元素)被取出,并去掉所有的
const或volatile限定符,结果类型被用于实例化正确版本的sig_helper类,后者具有正确的
typedef type. 这是为我们的类定义返回类型的一种相当复杂(但是必须!)的方法。
在C++中我们使用if-then-else,for,while, 等等
在 Boost.Lambda 中有所有的C++控制结构的lambda 版本。
std::vector<std::string>vec;
vec.push_back("Lambda");
vec.push_back("expressions");
vec.push_back("really");
vec.push_back("rock");
std::for_each(vec.begin(),vec.end(),if_then_else(
bind(&std::string::size,_1)<=6u,
std::cout << _1 << '\n',
std::cout <<constant("Skip.\n"))
);
std::for_each(vec.begin(),vec.end(),
if_(bind(&std::string::size,_1)<=6u)[
std::cout << _1 << '\n'
]
.else_[
std::cout <<constant("Skip.\n")
] );
1-二者等价:
2-控制结构的确增加了阅读 lambda 表达式的复杂度
3-含义:如果string元素的大小小于等于6,它们就被输出到std::cout; 否则,输出字符串"Skip"。
(if_then(_1<5,
std::cout << constant("Less than 5")))(make_const(3));