详解 C++11 lambda表达式

详解 C++11 lambda表达式

lambda表达式是函数式编程的基础。咱对于函数式编程也没有足够的理解,因此这里不敢胡言乱语,有兴趣的可以自己查找相关资料看下。这里只是介绍C++11中的lambda表达式以及与此相关的闭包(closure)。

 

其次,给出两个例子,可以看出lambda表达式的写法

[](int x, int y) { return x + y; }

[](int x, int y) -> int { int z = x + y; return z + x; } //表明返回值类型是int型

也就是说,一个lambda表达式很类似于普通的函数定义的写法,区别在于三点,一是没有函数名(你也可以认为函数名是[]),二是这个函数没有普通函数那样的返回值类型,返回值类型的写法在第2行,即在参数列表与函数定义之间以箭头写明,三是在参数列表之前有一个[] (其实这里还可能有更多的形式,后面会说)

 

第三,lambda表达式的标准形式是什么?

如下。这里先只是列出,后面会详细解释。

lambda-expression:
  lambda-introducer lambda-declaratoropt compound-statement
lambda-introducer:
  [ lambda-captureopt ]

lambda-capture:
  capture-default
  capture-list
  capture-default , capture-list
capture-default:
  &
  =
capture-list:
  capture
  capture-list , capture
capture:
  identifier
  & identifier
  this
lambda-declarator:
  ( parameter-declaration-clause ) attribute-specifieropt mutableopt exception-specificationopt trailing-return-typeopt


第四,进一步的分析

上面的标准形式中,最不好理解的可能就是lambda-capture了。其实它对应的就是闭包的概念,而作用是描述外部传入的参数。

wiki上有一个例子,如下

 
std::vector<int> someList;
int total = 0;
std::for_each(someList.begin(), someList.end(), [&total](int x) {
total += x;
});
std::cout << total;
 

这段代码做的事情是把someList中的所有变量求和。其中定义的lambda函数如下,

[&total](int x) {total += x;}

与前面的例子不同,这里的lambda-introducer是[&total]而不是之前的[]。这么写的作用是,在这个lambda函数中以引用方式使用外部变量total,这样求和结果就可以存放于这个变量中。那么类似的,还可以以传值方式使用外部变量,写成[total]就可以,而且,如果需要用到的外部变量较多,可以有简略的写法。一个完整的列表如下(来自wiki,这里我把他们翻译成我们比较习惯的表述形式)

 
[] // 没有定义任何变量。使用未定义变量会导致错误。
[x, &y] // x 以传值方式传入(默认),y 以引用方式传入。
[&] // 任何被使用到的外部变量皆隐式地以引用方式加以使用。
[=] // 任何被使用到的外部变量皆隐式地以传值方式加以使用。
[&, x] // x 显示地以传值方式加以使用。其余变量以引用方式加以使用。
[=, &z] // z 显示地以引用方式加以使用。其余变量以传值方式加以使用。
 


第五,代码练习及验证

c++ 将lambda表达式作为参数传递
1、获取变量类型
#include <typeinfo.h>
 
int a = 1;
 
typeid(a).name();


2、lambda表达式用法
C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。Lambda 表达式本质上与函数声明非常类似。
[capture](parameters)->return-type{body}
 
//例如
[](int x, int y) -> int { int z = x + y; return z + x; }
 
int main(){
  int a = 1;
  int b = 2;
 
  auto lambda = []{return a + b;}; 
  //error! 空捕获列表,无法使用作用域内其他变量
 
  auto lambda = [](int a, int b){return a + b;}; 
  //success
 
  auto lambda = [=]{return a + b;}; 
  //success, 值传递
 
  auto lambda = [=]{a++; b++; return a + b;}; 
  //error! 值传递无法修改变量值
 
  auto lambda = [&]{a++; b++; return a + b;}; 
  //success, 引用传递
 
  auto lambda = [&a, b]{a++; b++; return a + b;}; 
  //error, 变量a引用传递,变量b值传递,故b不可修改
 
}

 

C++ 使用lambda表达式作为函数参数

模板参数
#include <iostream>
#include <string>
 
template <typename F>
void print(F const &f){
    std::cout<<f()<<std::endl;
}
 
int main() {
    std::cout << "Hello, World!" << std::endl;
 
    int num = 101;
    auto a = [&]//以引用的方式捕获本函数中的变量
             () //无参数
             ->std::string {//返回值的类型为std::string
        return std::to_string(num);
    };
    print(a);
    num++;
    print(a);
 
    return 0;
}
调用print时,C++编译器会自动推断F的类型。运行上述程序,得到如下输出

Hello, World!
101
102
编译器需要支持c++11标准,有的编译器可能需要添加参数-std=c++11。

使用std::function
#include <iostream>
#include <functional>
#include <string>
 
void print(std::function<std::string ()> const &f){
    std::cout<<f()<<std::endl;
}
 
int main() {
    std::cout << "Hello, World!" << std::endl;
 
    int num = 101;
    auto a = [&]//以引用的方式捕获本函数中的变量
             () //无参数
             ->std::string {//返回值的类型为std::string
        return std::to_string(num);
    };
    print(a);
    num++;
    print(a);
 
    return 0;
}
stdL::function是个模板类;模板参数:返回值类型+函数参数类型列表;例如std::function<int  (int, int)> f,f可以指向任意一个返回指为int,参数为两个int类型的函数。

观察上面的程序,lambda表达式无参数,返回值类型为string;故在print函数中声明一个类型为function<std::string ()>类型的变量f,就可以用来接收a。注意:f前面必须加上const,否则编译过不了。至于加不加&,需要视程序而定,一般加上。

C++11 lambda表达式使用场景解析

Lambda表达式的使用场景

其实我在刚开始使用lambda的时候就挺好奇什么时候使用好,经常在不需要使用的时候使用上了,真的是多此一举,那么要在什么情况下使用呢?
因为lambda表达式又叫匿名函数(当然我们也可以理解为是一个未命名的内联函数),那么肯定就跟函数挂上关系了,通常情况写你在编程的时候需要将这段代码封装到一个函数里面再来调用,那这个时候就避免不了想函数名了,其实有时候取名字真的是件麻烦事,有时脑子突然短路都不知道命什么名字好了,那么这个时候你就要想起我们的lambda表达式了,它可以很好的帮你解决命名困难症这个问题。
那么还有什么情况下可以去考虑使用lambda表达式呢?在你的整个项目编程中,你独立出来一个函数,但这个函数实现相对简单并且可能在整个项目只使用了一次(即不存在复用的情况),那么这个时候我们就可以考虑使用下lambda表达式了,这样可以让代码更加紧凑,更加容易维护。

 

posted @ 2019-12-16 22:22  南哥的天下  阅读(957)  评论(0编辑  收藏  举报