lambda表达式(Anonymous function)是匿名函数,没有具体的函数名,一个lambda表达式表示一个可调用的代码单元,我们可以将它理解为未命名的内联函数(摘自C++ primer 5),一个lambda表达式具有一个返回类型,一个参数列表和一个函数体.
一个lambda的具体形式如下:
[ c a p t u r e l i s t ] ( p a r a m e t e r l i s t ) − > [ r e t u r n t y p e ] { f u n c t i o n b o d y } [capture\ list]\ \ (parameter\ list) ->[return\ type]\{\ function\ body\ \} [capture list] (parameter list)−>[return type]{ function body }
auto func = [] { return true }//忽略参数列表和返回类型,func则为可调用对象,不接受参数,返回true
auto func1 = [](const int &n){ return n > 0 ? true : false; };//判断n是否大于0
if (func(-2)) {
std::cout << "true" << std::endl;
}
捕获列表使用
lambda表达式中的捕获分为值捕获和引用捕获,[]空捕获列表,[=]隐式值捕获列表,[&]隐式引用捕获列表
捕获很好理解,类似于传值参数,使用值捕获时,并不是在lambda被调用的时候才会捕获,而是lambda创建时捕获
std::string test_message("good!");
//空捕获列表的lambda
auto show_capture1 = [] {
std::cout << "1: null capture" << std::endl;
return 0;
};
//隐式值捕获列表的lambda,此时lambda可以捕获当前空间内的所有变量
auto show_capture2 = [=] {
std::cout << "2: values capture\t" << test_message << std::endl;
};
//隐式引用捕获列表的lambda,此时lambda可以捕获当前空间内所有变量的引用
auto show_capture3 = [&] {
std::cout << "3: reference capture\t" << test_message << std::endl;
test_message = "awesome!";
};
//引用捕获test_message
auto show_capture4 = [&test_message] {
std::cout << "4: " << test_message << std::endl;
test_message = "bad!";
};
//值捕获test_message
auto show_capture5 = [test_message] {
std::cout << "5: " << test_message << std::endl;
// test_message = "123"; 这条语句编译错误具体错误看下文error1
};
//引用不糊test_message,其他变量采用值捕获
auto show_capture6 = [=, &test_message] {
std::cout << "6: " << test_message << std::endl;
};
//引用捕获其他变量,值捕获test_message
auto show_capture7 = [&, test_message] {
std::cout << "7: " << test_message << std::endl;
};
show_capture1();
show_capture2();
show_capture3();
show_capture4();
show_capture5();
show_capture6();
show_capture7();
输出:
1: null capture
2: values capture good!
3: reference capture good!
4: awesome!
5: good!
6: bad!
7: good!
分析上述代码:
1.由于前面说过,lambda值捕获是在lambda创建的时候进行的,当show_capture 2 5 7这三个lambda被调用时候,test_message的值为前面定义时候的值"good!".
2.当show_capture3被调用是,test_message的值为"good!“所以输出为"good!”,随后test_message的值被修改为"awesome!"
3.show_capture4传入的是test_message的引用,由于被3修改成"awesome!“所以输出为"awesome!”,随后test_message被修改为"bad!"
4.同上,当show_capture6被调用是,输出"bad!“因为test_message的值已经被show_capture4修改成"bad!”
list_learn.cc: In lambda function:
list_learn.cc:65:17: error: passing ‘const string {aka const std::__cxx11::basic_string}’ as ‘this’ argument discards qualifiers [-fpermissive]
test_message="123";
^
In file included from /usr/include/c++/5/string:52:0,
from /usr/include/c++/5/bits/locale_classes.h:40,
from /usr/include/c++/5/bits/ios_base.h:41,
from /usr/include/c++/5/ios:42,
from /usr/include/c++/5/ostream:38,
from /usr/include/c++/5/iostream:39,
from list_learn.cc:1:
/usr/include/c++/5/bits/basic_string.h:558:7: note: in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits; _Alloc = std::allocator] ’
operator=(const _CharT* __s)
出先上述错误的原因是我在show_capture5函数体里面对值捕获的test_message进行重新赋值是产生.
通过编译器看到错误提示为:
no operator “=” matches these operands – operand types are: const std::__cxx11::string = const char [4]
这里test_message的值拷贝是常量吗?
于是我修改一下代码,看看其他类型的值捕获会有什么奇怪的表现
std::string test_message("good!");
test_message="123";
int x=0;
auto show_capture5 = [x,test_message] {
std::cout << "5: " << test_message << std::endl;
// test_message="123";
x=123;// 这里提示expression must be a modifiable lvalue
};
这里说明x是一个不可以修改或者是一个右值,导致无法进行赋值.
查了一些博客,这篇博客提到:
Lambda表达式是通过生成一个小class实现的,这个类重载了operater()函数,所以它的行为非常像一个函数。Lambda函数就是这个class的实例;当这个类构造时,上下文中的任何变量都可以传进Lambda function class,并作为成员变量保存起来。这事实上这有点像一个已经存在的functor(仿函数)。这样就解决了我的问题,Capture用于传递上下文的环境变量,在class的构造函数中使用;Parameter list是operator()函数的参数列表。
因此我猜测,这个小class的所有成员变量都是被const修饰的,于是我尝试修改test_message的定义
const std::string test_message("good!");
test_message = "123";//提示错误 no operator "=" matches these operands -- operand types are: const std::__cxx11::string = const char [4]
编译时也是出现上述error,因此证明lambda的类实现里所有成员变量都是被const修饰的变量,其实这个问题查看lambda实现的原码就可以解,或者动脑子想想,修改值传递的拷贝值并没有意义,若要真正修改该变量的内容,可以采用引用捕获.