Lambda库

编写很多小的函数对象的缺点:

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=&sc;

 

以前:

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,包含了函数对象(第一个元素)和调用操作符的参数类型

tuple 作为sig的模板参数是如何工作

例子:

来看另一个有两个调用操作符的函数对象,其中一个版本接受一个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. 这是为我们的类定义返回类型的一种相当复杂(但是必须!)的方法。

Lambda 表达式中的控制结构

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函数

(if_then(_1<5,

std::cout << constant("Less than 5")))(make_const(3));




你可能感兴趣的:(Lambda库)