注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

C++11及BOOST特性之五lambda表达式  

2014-01-06 16:11:07|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++11BOOST特性之五lambda表达式

C++语言很强大,强大到绝大多数人不再敢于使用它。所以,它必须得转换,不然就会走向没落。所以,新标准开始支持一些动态语言中的方法。比如函数式编程中的lambda表达式。这样,就可以适应一些新技术的发展。

闲话不叙,先看一个最简单的例子

#include <iostream>   

int main() 

// 因为没有实际的参数,此处的小括号可省略

    auto myfun = [] () { std::cout << "Hello world"; };

    myfun(); // now call the function 

}

这就是c++lambada表达式的形式:[captures] (params) -> ret {Statments;}

现在看到Lambda是最简单的形式,只包含捕获子句和函数体两个必要部分,其他部分都省略了。[]Lambda的捕获子句,也是引出Lambda的语法,当编译器看到这个符号时,就知道我们在写一个Lambda了。函数体通过{} 包围起来,里面的代码和一个普通函数的函数体没有什么不同。象上面红色注释部分说的,如果没有参数,小括号可以省略

好,下面再看一个复杂一些的,假设有一个回调函数在下面的函数中使用:

Int GetData(int temp,[&count] (int num)->int

{

         If (num == 0){count--;return 0;}

})

这里面比上面有三个不同:一个是在中括号中有一个count的变量,然后小括号内有一个参数int num,然后在小括号后面有一个->int,他们的意思是这样的,前面表示引用的方式传递countlambada表达式的函数内使用,第二个表示这个表达式的函数有一个int型参数。第三个表示有一个int型的返回值

上面代码的是在Lambda的函数体里修改一个外部的变量,常见的语言(如C#)会自动为Lambda捕获当前上下文的所有变量,但C++要求我们在Lambda的捕获子句里显式指定想要捕获的变量,否则无法在函数体里使用这些变量。如果捕获子句里面什么都不写,比如最前面的代码所示的那样,编译器会认为我们不需要捕获任何变量。

      除了显式指定想要捕获的变量,C++还要求我们指定这些变量的传递方式,可以选择的传递方式有两种:按值传递和按引用传递。像[&count] 这种写法是按引用传递,这种传递方式使得你可以在Lambda的函数体里对count变量进行修改。相对的,如果变量名字前面没有加上"&"就是按值传递,这些变量在Lambda的函数体里是只读的。

      如果你希望按引用传递捕获当前上下文的所有变量,可以把捕获子句写成[&];如果你希望按值传递捕获当前上下文的所有变量,可以把捕获子句写成[=]。如果你希望把按引用传递设为默认的传递方式,同时指定个别变量按值传递,可以把捕获子句写成[&, a, b];同理;如果默认的传递方式是按值传递,个别变量按引用传递,可以把捕获子句写成[=, &a, &b]值得提醒的是,像[&, a, &b][=, &a, b]这些写法是无效的,因为默认的传递方式均已覆盖b变量,无需单独指定,有效的写法应该是[&, a][=, &a]

捕获变量的值什么时候确定?

看下面的代码,通过捕获子句而不是参数列表提供输入,这两个参数分别使用不同的传递方式,那么,在第三行修改这两个参数的值会否对第四行的调用产生影响?

 Int x =1,y = 3;

 Auto fun = [x,&y](){return x+y;};

X=3;y=4;

Cout<<fun()<<endl;

返回值是5,说明和平常的C++代码是一致的,函数传递进去几,值传递的话就是几,而引用则会跟着变化。

最后还要多说一句,有返回值 lambda表达式如何处理,上面已经有一个例子了,也有期末的形式,这里只说哪几种情况可以省略返回值:

第一种情况:返回值为void

第二种是情况:只包含一条返回语句:类似:Auto fun = [x,&y](){return x+y;};

下来再说说在标准库中的lambada表达式的使用一些特点,主要是std::thread这个模板类。

老规矩先看例子:

Int a =100;

std::thread _sendThread = std::thread([&](int & t){

                   int num = a; a++;

         },std::ref(a));

后面的参数引用传递使用std::ref的原因是thread是一个模板类,模板类对&引用符的处理有一些问题,所以在这个版本是用了一个关键字来处理。至于以后会不会解决这个问题,得看发展,比如模板中的>(右尖括号连写list<vector<string>> lvs;ISO98中会报编译错误)这个问题,就在新的版本中解决了。

如果大家有学习过C#的经验,那么对于接触这个非常地有感觉,网上也有人把二者的区别和相似点都列举了一下,大家可以自己去看看。

最后稍带脚的说一下这个std::thread,他可以使用普通函数,lambada表达式和仿函数,前两个都说过了,再说一下最后一个仿函数:

#include <iostream>

#include <thread>

 

void hello()

{

    std::cout << "Hello Concurrent World\n";

}

 

class background_task

{

public:

    void operator () () const

    {

       do_something();

       do_something_else();

    }

 

private:

    void do_something() const

    {

       std::cout << "void do_something()" << std::endl;

    }

    void do_something_else() const

    {

       std::cout << "void do_something_else()" << std::endl;

    }

};

 

int main()

{

    //std::thread t(hello);  

    background_task f;

    std::thread t(f);

    std::thread t2(background_task());

    //declares a function return a std::thread object

    t.join();

    t2.join();

}

 

注意:如果没有红色部分的const会报下面的错误:

thread.cpp: 在成员函数‘void background_task::operator()() const’中:

thread.cpp:14:21: 错误: 将‘const background_task’作为‘void background_task::do_something()’的‘this实参时丢弃了类型限定 [-fpermissive]

thread.cpp:15:26: 错误: 将‘const background_task’作为‘void background_task::do_something_else()’的‘this’实参时丢弃了类型限定 [-fpermissive]

 

thread.cpp: 在函数‘int main()’中:

thread.cpp:36:8: 错误: 对成员‘join’的请求出现在‘t2’中,而后者具有非类类型‘std::thread(background_task (*)())

May use this:

std::thread t2((background_task()));

std::thread t3{background_task()};//再说一下五中的对象定义:thread{…}这个是从GCC4.7以上及VS2013开始支持的。2014-04-28

http://www.it165.net/pro/html/201305/5960.html

事实证明第二种无法使用,也就是变黄色部分,其它都可以使用。

注意:2014-04-28说明http://www.haogongju.net/art/1945283

std::thread my_thread(background_task());

这种写法是不行的,因为编译器会以为你在声明一个函数,名字叫my_thread,一个参数一个返回值(std::thread

如果确实想用临时变量生成一个thread,那么可以用下面两个方法之一:

std::thread my_thread((background_task())); std::thread my_thread{background_task()};

或是lambda表达式:

std::thread my_thread([]( do_something(); do_something_else(); });

---------------------------------------------------------------------------------------------------------------------------------

技术之事,举于细节。

  评论这张
 
阅读(1180)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017