这个库用于解决一个使用标准库算法时常会遇见的问题,即需要为了满足算法的要求而定义很多简单的函数对象。几乎所有的标准库算法都有一个接受函数对象的版本,这个函数对象用于执行如排序、等同性检验、转换等操作。标准库通过绑定器 bind1st 和 bind2nd 支持有限的函数组合。但是,它们能做的事情非常有限,它们只能提供参数绑定,而不能绑定表达式。在 Boost.Lambda 库中,既有对绑定参数的灵活支持,也可以直接从表达式创建函数对象,对于C++标准库来说,这是一个杰出的合作伙伴。
Lambda
头文件: "boost/lambda/lambda.hpp"
它包括了本库的核心部分。
"boost/lambda/bind.hpp"
它定义了 bind 函数。
"boost/lambda/if.hpp"
它定义了相当于 if 的 lambda ,以及条件操作符。
"boost/lambda/loops.hpp"
它定义了循环结构(例如,while_loop 和 for_loop)。
"boost/lambda/switch.hpp"
它定义了相当于 switch 语句的 lambda 。
"boost/lambda/construct.hpp"
它定义了一些工具,为增加构造函数/析构函数以及 new/delete 。
"boost/lambda/casts.hpp"
它为提供了转型操作符。
"boost/lambda/exceptions.hpp"
它定义了在 lambda 表达式中进行异常处理的工具。
"boost/lambda/algorithm.hpp" and "boost/lambda/numeric.hpp"
它定义了用于嵌套函数调用的C++标准库算法的 lambda 版本(实际上就是函数对象)。
Boost.Lambda 用法
与其它许多 Boost 库一样,这个库完全定义在头文件中,这意味着你不必构建任何东西就可以开始使用。但是,知道一点关于 lambda 表达式的东西肯定是有帮助的。接下来的章节会带你浏览一下这个库,还包括如何在 lambda 表达式中进行异常处理!这个库非常广泛,前面还有很多强大的东西。一个 lambda 表达式通常也称为匿名函数(unnamed function)。它在需要的时 候进行声明和定义,即就地进行。这非常有用,因为我们常常需要在一个算法中定义另一个算法,这是语言本身所不能支持的。作为替代,我们通过从更大的范围引 进函数和函数对象来具体定义行为,或者使用嵌套的循环结构,把算法表达式写入循环中。我们将看到,这正是 lambda 表达式可以发挥的地方。本节内有许多例子,通常例子的一部分是示范如何用"传统"的编码方法来解决问题。这样做的目的是,看看 lambda 表达式在何时以及如何帮助程序写出更具逻辑且更少的代码。使用 lambda 表达式的确存在一定的学习曲线,而且它的语法初看起来有点可怕。就象每种新的范式或工具,它们都需要去学习,但是请相信我,得到的好处肯定超过付出的代价。
一个简单的开始
第一个使用 Boost.Lambda 的程序将会提升你对 lambda 表达式的喜爱。首先,请注意 lambda 类型是声明在 boost::lambda 名字空间中,你需要用一个 using 指示符或 using 声明来把这些 lambda 声明带入你的作用域。包含头文件 "boost/lambda/lambda.hpp" 就可以使用这个库的主要功能了,对于我们第一个程序这样已经足够了。
#include
#include "boost/lambda/lambda.hpp"
#include "boost/function.hpp"
int main() {
using namespace boost::lambda;
(std::cout << _1 << " " << _3 << " " << _2 << "!\n")
("Hello","friend","my");
boost::function f=
std::cout << _1 << "*" << _2 << "+" << _3
<< "=" <<_1*_2+_3 << "\n";
f(1,2,3);
f(3,2,1);
}
Hello my friend!
boost::function f=
std::cout <<
_1 << "*" << _2 << "+" << _3 << "=" <<_1*_2+_3 << "\n";
1*2+3=5
3*2+1=7
#include
#include
#include
What's wrong with the following expression?
key=0, value=Nothing, if you ask me
3, value=Less than pi
42, value=You tell me
...and why does this work as expected?
key=0, value=Nothing, if you ask me
key=3, value=Less than pi
key=42, value=You tell me
keys_and_values.size()=3
keys_and_values.max_size()=4294967295
class double_it {
public:
int operator()(int i) const {
return i*2;
}
};
int main() {
using namespace boost::lambda;
double_it d;
int i=12;
// If you uncomment the following expression,
// the compiler will complain;
// it's just not possible to deduce the return type
// of the function call operator of double_it.
// (std::cout << _1 << "*2=" << (bind(d,_1)))(i);
(std::cout << _1 << "*2=" << (bind(d,_1)))(i);
(std::cout << _1 << "*2=" << (ret(bind(d,_1))))(i);
}
#include
#include
#include
#include "boost/lambda/lambda.hpp"
boost::lambda::placeholder1_type Arg1;
boost::lambda::placeholder2_type Arg2;
boost::lambda::placeholder3_type Arg3;
template
void for_all(T& t,Operation Op) {
std::for_each(t.begin(),t.end(),Op);
}
int main() {
std::vector vec;
vec.push_back("What are");
vec.push_back("the names");
vec.push_back("of the");
vec.push_back("placeholders?");
for_all(vec,std::cout << Arg1 << " ");
std::cout << "\nArg1, Arg2, and Arg3!";
}
What are the names of the placeholders?
Arg1, Arg2, and Arg3!
#include
#include
#include
#include "boost/lambda/lambda.hpp"
int main() {
using boost::lambda::constant;
using boost::lambda::constant_type;
constant_type::type newline(constant('\n'));
constant_type::type space(constant(' '));
boost::lambda::placeholder1_type _;
std::vector vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
for_all(vec,std::cout << space << _ << newline);
for_all(vec,
std::cout << constant(' ') << _ << constant('\n'));
}
#include
#include
#include
#include "boost/lambda/lambda.hpp"
template class memorizer {
std::vector vec_;
public:
memorizer& operator=(const T& t) {
vec_.push_back(t);
return *this;
}
void clear() {
vec_.clear();
}
void report() const {
using boost::lambda::_1;
std::for_each(
vec_.begin(),
vec_.end(),
std::cout << _1 << ",");
}
};
int main() {
using boost::lambda::var_type;
using boost::lambda::var;
using boost::lambda::_1;
std::vector vec;
vec.push_back(0);
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
memorizer m;
var_type >::type mem(var(m));
std::for_each(vec.begin(),vec.end(),mem=_1);
m.report();
m.clear();
std::for_each(vec.begin(),vec.end(),var(m)=_1);
m.report();
}
#include
#include
#include
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
void plain_function(int i) {
std::cout << "void plain_function(" << i << ")\n";
}
class some_class {
public:
void member_function(int i) const {
std::cout <<
"void some_class::member_function(" << i << ") const\n";
}
};
int main() {
std::vector vec(3);
vec[0]=12;
vec[1]=10;
vec[2]=7;
some_class sc;
some_class* psc=≻
// Bind to a free function using ptr_fun
std::for_each(
vec.begin(),
vec.end(),
std::ptr_fun(plain_function));
// Bind to a member function using mem_fun_ref
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun_ref(&some_class::member_function),sc));
// Bind to a member function using mem_fun
std::for_each(vec.begin(),vec.end(),
std::bind1st(
std::mem_fun(&some_class::member_function),psc));
using namespace boost::lambda;
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));
}
#include
#include
#include
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
std::vector vec(3);
vec[0]=12;
vec[1]=10;
vec[2]=7;
// Transform using std::bind1st and std::plus
std::transform(vec.begin(),vec.end(),vec.begin(),
std::bind1st(std::plus(),4));
// Transform using a lambda expression
std::transform(vec.begin(),vec.end(),vec.begin(),_1-=4);
}
#include
#include
#include
#include
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_;
}
};
int main() {
std::vector vec;
vec.push_back(search_for_me("apple","banana"));
vec.push_back(search_for_me("orange","mango"));
std::vector::iterator it=
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::iterator it=
std::find_if(vec.begin(),vec.end(),a_finder("apple"));
std::vector::iterator it=
std::find_if(vec.begin(),vec.end(),
bind(&search_for_me::a,_1)=="apple");
#include
#include
#include
#include
#include "boost/lambda/lambda.hpp"
int main() {
using namespace boost::lambda;
std::vector vec1;
vec1.push_back(2);
vec1.push_back(3);
vec1.push_back(5);
vec1.push_back(7);
vec1.push_back(11);
std::vector vec2;
vec2.push_back(7);
vec2.push_back(4);
vec2.push_back(2);
vec2.push_back(3);
vec2.push_back(1);
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';
}
template class add_prev {
T prev_;
public:
T operator()(T t) {
prev_+=t;
return prev_;
}
};
#include
#include
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
int main() {
using namespace boost::lambda;
std::vector vec;
vec.push_back(5);
vec.push_back(8);
vec.push_back(2);
vec.push_back(1);
add_prev ap;
std::transform(
vec.begin(),
vec.end(),
vec.begin(),
bind(var(ap),_1));
}
template class add_prev :
public std::unary_function {
T prev_;
public:
template class sig {
public:
typedef T type;
};
// Rest of definition
template class sig_helper {};
// The version for the overload on int
template<> class sig_helper {
public:
typedef std::string type;
};
// The version for the overload on std::string
template<> class sig_helper {
public:
typedef double type;
};
// The function object
class some_function_object {
template class sig {
typedef typename boost::tuples::element<1,Args>::type
cv_first_argument_type;
typedef typename
boost::remove_cv::type
first_argument_type;
public:
// The first argument helps us decide the correct version
typedef typename
sig_helper::type type;
};
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;
}
};
Lambda 表达式中的控制结构
我们已经看到强大的 lambda 表达式可以很容易地创建,但是许多编程上的问题需要我们可以表示条件,在C++中我们使用 if-then-else, for, while, 等等。在 Boost.Lambda 中有所有的C++控制结构的 lambda 版本。要使用选择语句,if 和 switch, 就分别包含头文件 "boost/lambda/if.hpp" 和 "boost/lambda/switch.hpp"。要使用循环语句,while, do, 和 for, 就包含头文件 "boost/lambda/loops.hpp". 关键字不能被重载,所以语法与你前面使用过的有所不同,但是也有很明显的关联。作为第一个例子,我们来看看如何在 lambda 表达式中创建一个简单的 if-then-else 结构。格式是 if_then_else(条件, then-语句, else-语句)。还有另外一种语法形式,即 if_(条件)[then-语句].else_[else-语句] 。
#include
#include
#include
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"
#include "boost/lambda/if.hpp"
int main() {
using namespace boost::lambda;
std::vector 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")
] );
}
if_then_else(
bind(&std::string::size,_1)<=6u,
std::cout << _1 << '\n',
std::cout << constant("Skip.\n")));
(if_then_else(
_1==0,
std::cout << constant("Nothing"),
std::cout << _1))(make_const(0));
(if_(_1==0)
[std::cout << constant("Nothing")].
else_[std::cout << _1])(make_const(0));
int i;
int value=12;
var(i)=(if_then_else_return
(_1>=10,constant(10),_1))(value);
(switch_statement
_1,
case_statement<0>
(var(std::cout) << "Nothing"),
case_statement<1>
(std::cout << constant("A little")),
default_statement
(std::cout << _1))
)(make_const(100));
int val1=1;
int val2=4;
(while_loop(_1<_2,
(++_1,std::cout << constant("Inc...\n"))))(val1,val2);
int val1=1;
int val2=4;
(while_(_1<_2)
[++_1,std::cout << constant("Inc...\n")])(val1,val2);
int val1=0;
var_type::type counter(var(val1));
(for_loop(counter=0,counter<_1,++counter,var(std::cout)
<< "counter is " << counter << "\n"))(make_const(4));
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/casts.hpp"
#include "boost/lambda/if.hpp"
#include "boost/lambda/bind.hpp"
class base {
public:
virtual ~base() {}
void do_stuff() const {
std::cout << "void base::do_stuff() const\n";
}
};
class derived : public base {
public:
void do_more_stuff() const {
std::cout << "void derived::do_more_stuff() const\n";
}
};
int main() {
using namespace boost::lambda;
base* p1=new base;
base* p2=new derived;
derived* pd=0;
(if_(var(pd)=ll_dynamic_cast(_1))
[bind(&derived::do_more_stuff,var(pd))].
else_[bind(&base::do_stuff,*_1)])(p1);
(if_(var(pd)=ll_dynamic_cast(_1))
[bind(&derived::do_more_stuff,var(pd))].
else_[bind(&base::do_stuff,*_1)])(p2);
}
#include
#include
#include
#include "boost/lambda/lambda.hpp"
#include "boost/lambda/casts.hpp"
#include "boost/lambda/if.hpp"
#include "boost/lambda/bind.hpp"
template
void is_it_long(const String& s,const Integral& i) {
using namespace boost::lambda;
(if_then_else(bind(&String::size,_1)<_2,
var(std::cout) << "Quite short...\n",
std::cout << constant("Quite long...\n")))(s,i);
}
int main() {
std::string s="Is this string long?";
is_it_long(s,4u);
is_it_long(s,4);
}
"An object" already has a valid pointer.
"Another object" already has a valid pointer.
"Yet another object" already has a valid pointer.
Here we go again...
"An object" already has a valid pointer.
"Another object" already has a valid pointer.
Created a new derived for "Beware, this is slightly tricky".
"Yet another object" already has a valid pointer.
Throw an exception here.
Caught exception, "Somewhere, something went terribly wrong."