服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - 一文详解C++11中的lambda函数

一文详解C++11中的lambda函数

2023-02-28 14:10Shawn-Summer C/C++

小编可以明确告诉大家:lambda函数是C++11中最重要的,使用最广泛的,最具现代风格的内容,lambda函数的出现改变了C++编程的思维方式。所以快和小编学习一下C++11中lambda函数的使用吧

我可以明确告诉你:lambda函数是C++11中最重要的,使用最广泛的,最具现代风格的内容,lambda函数的出现改变了C++编程的思维方式。

?
1
2
3
4
5
6
7
8
#include<iostream>
using namespace std;
int main()
{
    int girls=3,boys=4;
    auto totalChild=[](int x,int y)->int{return x+y;};
    return totalChild(girls,boys);
}

上面中,auto和lambda函数的配合是一种经典生成局部函数的方法。lambda函数和普通函数最大的不同的地方就是它没有名字,所以 lambda函数是右值。

lambda函数相较于,函数指针,仿函数,它不仅简单,而且效率高。

1.lambda函数语法

[capture](parameters)mutable ->return-type{statement}

  • [capture]:捕捉列表。捕获父作用域中可用的变量,供lambda函数使用,[]是lambda函数的导出符号。
  • (parameters):参数列表。和普通函数的参数列表一样,如果没有参数可以省略
  • mutable:修饰符号。lambda函数默认是一个const修饰的函数,mutab可以取消其常量性
  • ->return-type:返回类型。类似于返回值后置的语法,如果没有返回值或者返回类型可自动推断就可省略声明返回类型。
  • {statement}:函数体。

根据上面语法,我们知道[]{}是一种最简单的lambda函数,而且我们发现,从语法角度来看,lambda函数比普通函数多了一个捕获列表,这是它的精髓所在。

1.1 捕获列表

?
1
2
3
4
5
6
7
8
9
int main()
{
    []{};//最简单的lambda函数
    int a=3;
    int b=4;
    [=]{return a+b;};
    auto func1=[&](int c){b=a+c;};
    auto func2=[=,&b](int c)->int{return b+=a+c;};
}。

上面代码中,我们可以使用捕获列表来捕获,变量a和b

被捕获的变量和基于参数传递的变量是不同的,被捕获的变量更是一种lambda函数的初始状态。

捕获列表是由多个捕获项组成的:

  • [var]:表示按值方式捕获变量var
  • [=]:表示按值捕获其父作用域中所有可用的变量(包括this)
  • [&var]:表示按引用捕获变量var
  • [&]:表示按引用捕获其父作用域中所有可用的变量(包括this)
  • [this]:表示按值传递当前的this指针

特别的这些捕获项可用组合使用,例如:[=,&a,&b]表示以引用捕获a和b,按值捕获其他所有变量。[&,a,this]表示按值捕获a和this,按引用捕获其他所有变量。

下面看一段代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
using namespace std;
 
int main()
{
    int j=12;
    auto fun1=[=]{return j;};
    auto fun2=[&]{return j;};
    cout<<"fun1: "<<fun1()<<endl;
    cout<<"fun2: "<<fun2()<<endl;
 
    j++;
    cout<<"fun1: "<<fun1()<<endl;
    cout<<"fun2: "<<fun2()<<endl;
}
/*
fun1: 12
fun2: 12
fun1: 12
fun2: 13
*/

当j++后,再次调用func1()时,我们发现它里面的j却保持不变,所以上面这段代码反应了一个事实:捕获的变量是lambda函数的初始状态。

在使用lambda函数时,按值传递的变量成为函数中的常量,它不会再运行过程中改变,按引用传递的变量,它类似于函数参数,他会随时检查其值。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
int temp=0;
int main()
{
    static int a=0;
    auto fun=[]{temp++;a++;};
    fun();
    fun();
    cout<<temp<<endl;//2
    cout<<a<<endl;//2;
 
    [temp]{};//编译错误
    [a]{};//编译错误
}

lambda函数中也可以直接使用全局变量,但是如果fun1这样就是错误的,因为 lambda函数只能捕获其父作用域中可用的自动变量,而静态变量不需要捕获,可以直接使用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
using namespace std;
int main()
{
    int a=1;
    cout<<"a="<<a<<endl;
    auto foo1=[&]()
    {
        a++;
        cout<<"a="<<a<<endl;
        auto foo2=[&]()
        {
            a++;
            cout<<"a="<<a<<endl;
        };
        foo2();
    };
    foo1();
}
/*
a=1
a=2
a=3
*/

上面代码中说明,lambda函数中还可以使用lambda函数,这样子,上面代码就狠像pascal语言中的内嵌函数。

1.2 mutable修饰符

lambda函数是具有常量性的,下面这段代码是在stackoverflow网站中的一次讨论:

?
1
2
3
4
5
6
7
8
9
int main()
{
    int val;
    auto fun1=[=]{val=3;};//编译失败,val无法被赋值
    auto fun2=[=]() mutable {val=3;};
    auto fun3=[&]{val=3;};
    auto fun4=[](int v){v=3;};
    fun4(val);
}

上面中,fun1无法通过编译,因为val是按值传递的,所以在函数体中,val就是一个只读常量,无法对其进行赋值,我们可以使用修饰符mutable来取消其只读属性。这样的目的是只是提供一种语法上的可能,在实际使用的时候,我们一般不需要使用mutable,如果需要修改按值传递的值,我们可以直接按值传递参数,而不是捕获它。

实际上,lambda函数中的捕获变量,更像是函数对象(仿函数)中的私有数据成员:

?
1
2
3
4
5
6
7
8
class fun1
{
private:
    int val;
public:
    fun1(int v):val(v){};
    void operator()const{val=3;};//编译出错
}

默认情况下,按值捕获的变量,如果不加mutable,它就会等价于上面的仿函数,这里的operator()就是const修饰的,它不允许修改val。

1.3 匿名lambda函数

lambda函数本身是右值,它没有名字,它本身就是匿名的,我们一般通过auto来赋予它一个名字,这样就能生成类似一个局部函数的效果。我们也可以不使用auto,我们可以直接生成一个lambda函数,然后调用,例如下面这段代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;
 
int main()
{
    const int a=[]{
        int ret=0;
        for(int i=0;i<100;i++)
        {
            ret+=i;
        }
        return ret;
    }();
    cout<<a<<endl;
}

lambda函数定义后直接调用。

实际上,lambda函数的设计初衷就是:就地书写,就地使用。所以诸如上面的写法非常常见。

2.lambda与STL

lambda函数的出现,让我们发现使用STL算法更加简单了。

例如for_each()算法,它接收3个参数,前两个是指示范围的迭代器类型,第3个是接收一个参数的函数符(即仿函数,函数指针,lambda函数)。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
extern vector<int> nums;
 
void OneCond(int val)
{
    //传统for方法
    for(auto i=nums.begin();i!=nums.end();++i)
    {
        if(*i==val)
            break;
    }
    //使用adapter
    find_if(nums.begin(),nums.end(),bind2nd(equal_to<int>(),val));
    //使用lambda函数
    find_if(nums.begin(),nums.end(),[=](int i){
        return i==val;
    });
}

上面代码中,有些人认为使用这种adapter会简单一点,就像这里的equal_to<int>()它就是是一个函数对象,我只能说仁者见仁。但是这种使用adapter的方式创建函数对象,要求程序员懂很多STL的知识,而且可读性不好,例如下面这段代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
 
extern vector<int> nums;
 
void twoCond(int low,int high)
{
    for(auto i=nums.begin();i!=nums.end();i++)
    {
        if(*i>=low && *i<high)break;
    }
    find_if(nums.begin(),nums.end(),compose2(
        logical_and<bool>(),
        bind2nd(less<int>(),high),
        bind2nd(greater_equal<int>(),low)
    ));
    find_if(nums.begin(),nums.end(),[=](int i)
    {
        return i>=low && i<high;
    });
}

再看看下面的lambda简化STL的例子

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
 
vector<int> nums;
void Add(const int val)
{
    auto print =[]{
        for(auto s:nums)
        {
            cout<<s<<"\t";
        }
        cout<<endl;
    };
    for(auto i=nums.begin();i!=nums.end();i++)
    {
        *i=*i+val;
    }
    print();
 
    for_each(nums.begin(),nums.end(),bind2nd(plus<int>(),val));
    print();
 
    transform(nums.begin(),nums.end(),nums.begin(),bind2nd(plus<int>(),val));
    print();
 
    for_each(nums.begin(),nums.end(),[=](int &i){i+=val;});
    print();
}
int main()
{
    for(int i=0;i<10;i++)
    nums.emplace_back(i);
    Add(10);
}
/*
10      11      12      13      14      15      16      17      18      19
10      11      12      13      14      15      16      17      18      19
20      21      22      23      24      25      26      27      28      29
30      31      32      33      34      35      36      37      38      39
*/

我们发现上面代码运行后,第二行相较于第一行没有变化,如果熟悉STL,你会狠容易发现,因为for_each()它不会写回,而transform它会写回。STL新手就会容易犯这个错误,如果你使用lambda函数这些东西就不需要了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<vector>
#include<algorithm>
#include<iostream>
#include<numeric>
using namespace std;
 
void Stat(vector<int> &v)
{
    int errors;
    int score;
    auto print =[&]{
        cout<<"Errors: "<<errors<<endl
        <<"Score: "<<score<<endl;
    };
    errors=accumulate(v.begin(),v.end(),0);
    score=accumulate(v.begin(),v.end(),100,minus<int>());
    print();
 
    errors=0;
    score=100;
 
    for_each(v.begin(),v.end(),[&](int i){
        errors+=i;
        score-=i;
    });
    print();
}
 
int main()
{
    vector<int> v(10);
    generate(v.begin(),v.end(),[]{return rand()&10;});
    Stat(v);
}

总之,lambda函数非常好用

以上就是一文详解C++11中的lambda函数的详细内容,更多关于C++11 lambda函数的资料请关注服务器之家其它相关文章!

原文链接:https://blog.csdn.net/m0_71009069/article/details/128881416

延伸 · 阅读

精彩推荐
  • C/C++C++ 实战开发一个猜单词的小游戏

    C++ 实战开发一个猜单词的小游戏

    众所周知纸上得来终觉浅,我们要在实战中才能真正的掌握技术,小编为大家带来一份用C++编写的猜单词小游戏,给大家练练手,快来看看吧...

    薛定谔的猫~6592022-02-24
  • C/C++C++实操之内联成员函数介绍

    C++实操之内联成员函数介绍

    大家好,本篇文章主要讲的是C++实操之内联成员函数介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览...

    夜流冰10432022-07-22
  • C/C++Cocos2d-x学习笔记之世界坐标系、本地坐标系、opengl坐标系、屏幕坐标系

    Cocos2d-x学习笔记之世界坐标系、本地坐标系、opengl坐标系、屏幕

    这篇文章主要介绍了Cocos2d-x学习笔记之世界坐标系、本地坐标系、opengl坐标系、屏幕坐标系,本文用代码和注释讲解了Cocos2d-x中的坐标体系,需要的朋友可以...

    C语言程序设计7762021-02-01
  • C/C++C C++ 算法实例大全

    C C++ 算法实例大全

    这篇文章主要介绍了C C++ 算法实例大全,里面大量的实例介绍,学习c语言的朋友可以收藏...

    99re9682021-04-21
  • C/C++深入解析C++11 lambda表达式/包装器/线程库

    深入解析C++11 lambda表达式/包装器/线程库

    这篇文章主要介绍了C++11 lambda表达式/包装器/线程库的相关知识,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值...

    可口也可樂11552022-12-06
  • C/C++c++primer类详解

    c++primer类详解

    今天小编就为大家分享一篇关于C++Primer中变量和基本类型的文章,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小...

    旋转吧!风火轮8512022-01-12
  • C/C++C语言实现输入两个数字将其按从小到大输出的方法

    C语言实现输入两个数字将其按从小到大输出的方法

    这篇文章主要介绍了C语言实现输入两个数字将其按从小到大输出的方法,本文通过代码讲解的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需...

    橗釉3972021-11-01
  • C/C++C++位运算符详解(异或运算符和移位运算符)

    C++位运算符详解(异或运算符和移位运算符)

    下面小编就为大家带来一篇C++位运算符详解(异或运算符和移位运算符)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看...

    C++教程网6482021-04-05