Effective C++: lambda表达式与闭包.

C++11:带来了lambda表达式.那么让我们一起来探究吧.

lambda捕获列表:

1,  []   空的捕获列表,lambda不能使用所在函数内的任何变量.

2, [names] names是一个用逗号分隔的名字列表,这些名字都是lambda所在函数的局部变量,默认情况都是以拷贝的性质捕获(capture)的,如果这些名字前面都有&那么就都是以引用的形式捕获的.

3, [&] 隐式的以引用形式的列表捕获,lambda体内所有的来自函数的实体都是以引用的形式捕获.

4, [=] 隐式的以拷贝形式的列表捕获,lambda体内的所有来自函数的实体都是以值的形式捕获的.

5, [&, names] names是一个逗号分隔的名字列表,包含0个或多个来自函数的变量,这些变量都是以拷贝的形式被捕获的,names中变量的名字前面不能使用&. 那些没有出现在names中,却出现在了lambda函数体变量都是以引用的形式捕获的.

6, [=, names] names是一个逗号分隔的名字列表,这些名字都是lambda所在的函数的局部变量,且这些名字前面必须有&,列表内的变量都是以引用的形式捕获的,那些没有出现在names中,却出现在lambda函数体中的变量都是以拷贝的形式捕获的.

7, [name] name是一个变量的名字,以拷贝的形式捕获该变量.

8, [&name] name是一个变量的名字,以引用的形式捕获该变量.

9, [this] this就是在一个类内捕获指向该类的指针以拷贝的形式捕获.

 

lambda的性质:

lambda没有固定的类型只能通过decltype来推断,标准规定lambda没有具体的类型,不是union,不是聚合类型,不是pr,不是某些特定类型的operator().

 

lambda的闭包特性:

 

Lambda表达式则是C++中的新语法,但是并没有许多程序员渴望的部分闭包特性(由于RAII的缘故)。C++中Lambda表达式可以被视为一种匿名函数,这样,对于一些非常短,而且不太可能被其他地方的复用的小函数,可以通过Lambda表达式提高代码的可读性。

  在Lambda表达式中对于变量生命期的控制还是与完全支持闭包的JavaScript非常不同,总而言之,C++对于变量声明期的控制在新标准中完全向前兼容,也就是局部变量一定在退出代码块时被销毁,而不是观察其是否被引用。因此,尽管C++的Lambda表达式中允许引用其代码上下文中的值,但是实际上并不能够保证引用的对象一定没有被销毁。

  Lambda表达式对于上下文变量的引用有值传递和引用传递两种方式,实际上,无论是哪种方式,在产生Lambda表达式对象时,这些上下文值就已经从属于Lambda表达式对象了,也就是说,代码运行至定义Lambda表达式处时,通过值传递方式访问的上下文变量值已经被写入Lambda表达式的栈中,而引用方式传递的上下文变量地址被写入Lambda表达式的栈中。因此,调用Lambda表达式时得到的上下文变量值就是定义Lambda表达式时这些变量的值,而引用的上下文变量,如果已经被销毁,则会出现运行时异常

 

#include <iostream>
#include <vector>
#include <functional>
#include <memory>
std::vector<std::function<void(const int&)>> filter;
class Widget{
 private:
  int divisor;
  
  public:
   Widget(const int& number=20):divisor(number){};
   
   ~Widget(){ std::cout<<"destroy!"<<std::endl; };
   
   void addFilter()const noexcept;
};
void Widget::addFilter()const noexcept
{
 auto ptr = this;
 auto n1 = 10;
 auto n2 = 20;
 filter.emplace_back([this](const int& value){ std::cout<<value<<"  "<<this->divisor<<std::endl; });
 //filter.emplace_back([ptr](const int& value){ std::cout<<value<<"  "<<ptr->divisor<<std::endl; }); //跟上面的效果实际上是一样的. 
 
 filter.emplace_back([&](const int& value){ std::cout<<n1<<" "<<n2<<std::endl; });
 filter.emplace_back([=](const int& vale){ std::cout<<n1<<" "<<n2<<std::endl; });
 
}
void addFilter()noexcept
{
 std::shared_ptr<Widget> ptr = std::make_shared<Widget>();
 ptr->addFilter();
}
int main()
{
 addFilter();
 
 //case 1:
 (filter[0])(20);
 
 //case 2:
 (filter[1])(30); //产生未定义行为:因为引用的n1和n2在函数addFilter()结束的时候被销毁了.
 
 //case 3:
 (filter[2])(40); //OK, 因为是拷贝的形式. 
  
  //case 4:
 int n1 = 1;
 auto f0 = [&n1]() { n1+=10; };
 f0();
 std::cout<<n1<<std::endl; //输出:11. 
 
 //case 5:
 int n2 = 2;
 std::function<int()> f1 = [&n2]()->int { n2+=10; std::cout<<"enter!"<<std::endl; return n2; };
 std::cout<<f1()<<"  "<<n2<<std::endl; //输出: 12  2; 
  //之所以会这样是因为 相当于std::cout<< (n2 += 10) << n2 << std::endl; 这是未定义的行为
 
 //case 6:
 int n3 = 3;
 std::function<int()> f2 = [&n3]()->int { n3+=10; return n3; };
 f2();
 std::cout<<n3<<std::endl; //输出: 13; 
 
 //case 7:
 int n4 = 4;
 std::function<int()> f3 = [n4]()mutable->int { n4+=10; return n4; }; //这里必须用mutable才能修改拷贝捕获过来的变量.
 f3();                                                     
 std::cout<<f3()<<" " <<n4<<std::endl; //输出: 24 2; 由此可以看出lambda是一个状态机. 
 
 //case 8:
 int n5 = 5;
 auto f4 = [n5]()mutable->int { n5+=10; return n5; };
 f4();                                
 std::cout<<f4()<<" "<<n5<<std::endl; // 输出: 25 5; 再次看出lambda是个状态机. 
 
 //case 9:
 std::unique_ptr<int> ptr(new int(300));
 auto f5 = [ptr = std::move(ptr)]() { std::cout<<*ptr<<std::endl; }; //dev c++下只能用 auto. 
 //std::function<void()> f5 = [ptr = std::move(ptr)](){ std::cout << *ptr << std::endl; }; //vs和dev c++都不能用. 
 f5(); //输出: 300; 
 
 return 0;
}


 

 

转载于:https://my.oschina.net/SHIHUAMarryMe/blog/669919

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值