您的位置:首页 > 编程语言 > C语言/C++

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

2016-07-03 22:28 113 查看
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: