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的性质:
1,lambda是一个状态机!!!!!!!!
2,lambda没有固定的类型只能通过decltype来推断,标准规定lambda没有具体的类型,不是union,不是聚合类型,不是pr,不是某些特定类型的operator().
lambda的闭包特性:
Lambda表达式则是C++中的新语法,实现了许多程序员渴望的部分闭包特性。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; 这种情况在msvs2016的情况下是错误的.
//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;
}