C++11中匿名函数lambda的使用详解

 更新时间:2023年04月27日 10:53:41   作者:Thomas_Lbw  
我最早接触lambda的概念是在matlab中,那时候在做数值模拟的课题,lambda可以快速定义简单的函数,当时觉得好方便。任何语言都有这个功能,下面来看看C++11新引入的lambda是如何使用的吧

官方介绍:

C++ lambda是C++11新增的一种匿名函数的实现方式,可以在代码中直接定义一个函数对象。它的语法是通过关键字“[]”来定义的,括号里可以包含需要访问的外部变量。Lambda函数可以用于在STL算法中提供自定义的比较函数,或者作为std::function的参数。它的简洁和易用性使得在C++11中变得非常流行。

一、lambda基础介绍

Lambda表达式的完整声明格式如下:

[capture list](parameter list) mutable exception->
     return type { function body }
  • capture list: 用于捕获外部变量的列表,可以省略。
  • parameter list: 函数参数列表,可以省略。
  • mutable: 可选项,用于指定能否修改捕获的变量。
  • exception: 可选项,用于指定能抛出的异常。
  • return type: 可选项,用于指定返回值类型。如果省略,编译器会自动推断。
  • function body: 函数主体。

通常情况下,一个 lambda 表达式不需要如此多的关键词,更常用的声明形式如下,只需要captures、parameters、return type、body即可:

[captures] (parameters) -> return_type {body}

有一些情况下,返回类型可以从 return 语句中推导(如 auto),则返回类型可以省略。而当 lambda 表达式没有参数的时候,用于表示参数列表的括号也是可以省略的:

[captures] {body}
 
//也就是
[]{}

具体的例子:

[](){}   //定义一个没有参数和返回值的lambda表达式
[](int a, int b){ return a+b;}   //定义一个带有参数的lambda表达式
[x](int a){ return x*a;}   //定义一个捕获x的lambda表达式

Lambda表达式还可以使用默认参数值,通过在参数后面使用 "= default_value"来实现。

例如 :

[](int a, int b = 10){ return a + b;}  //定义一个带有默认参数值的lambda表达式

在捕获列表中, 你可以使用&或=来捕获外部变量。

  • & 使得lambda表达式可以修改捕获的变量。
  • = 使得lambda表达式不能修改捕获的变量(默认值)。

例如 :

int x = 10;
[&x](){x++;}  //定义一个可以修改x的lambda表达式
[=x](){x++;}  //定义一个不能修改x的lambda表达式

Lambda表达式可以通过赋值给std::function对象或者直接调用来使用。

例如 :

std::function<int(int, int)> add = [](int a, int b){ return a + b;};
int result = add(1, 2);

二、lambda使用例子

2.1 STL算法中的回调函数

STL算法如sort, for_each等需要一个可调用的对象来完成操作,这时候lambda表达式可以提供一种简洁的方式来定义这个可调用的对象。

#include <algorithm>
#include <iostream>
#include <vector>
 
int main()
{
    std::vector<int> v{3, 1, 4, 1, 5, 9, 2, 6, 5};
    std::sort(v.begin(), v.end(), [](int a, int b){ return a < b;});
    for(int x : v)
        std::cout << x << " ";
    std::cout << std::endl;
    return 0;
}

sort第三个参数可以传入一个函数,是因为 sort 使用了模板,第三个参数是一个比较函数,它是一个可以接受两个参数并返回一个bool值的函数。sort函数使用这个比较函数来确定如何排序序列中的元素。如果第三个参数省略,sort默认使用“小于”运算符进行比较。

可以使用 std::less<int> 替换 lambda 表达式,如下所示:

std::sort(v.begin(), v.end(), std::less<int>());

或者你也可以定义一个比较函数来代替 lambda:

bool myCompare(int a, int b) 
{ return a < b; }
 
 std::sort(v.begin(), v.end(), myCompare);

2.2 回调函数

在许多情况下,我们需要在某个对象的回调函数中进行某些操作,这时候lambda表达式可以提供一种简洁的方式来定义回调函数。

#include <iostream>
 
class Button
{
public:
    void setOnClick(std::function<void()> onClick) { m_onClick = onClick; }
    void click() { m_onClick(); }
private:
    std::function<void()> m_onClick;
};
 
int main()
{
    Button btn;
    btn.setOnClick([](){std::cout << "Button clicked" << std::endl;});
    btn.click();
    return 0;
}

"setOnClick" 函数接收一个 std::function<void()> 类型的参数,并将其存储在类的私有成员变量 "m_onClick" 中。std::function 是一种 C++11 标准库中的类型,它可以存储可调用的对象,如函数指针、函数对象、Lambda 表达式等,这段代码它存了一个void()的类型的调用。

"click" 函数调用了 "m_onClick",也就是之前设置的 std::function<void()> 类型的回调函数。

在 main 函数中,一个 Button 对象 "btn" 被创建并初始化。 然后调用 btn.setOnClick({std::cout << "Button clicked" << std::endl;}); 这个方法,将一个 lambda 表达式作为参数设置给 m_onClick。 最后调用 btn.click(); 触发了上面设置的 lambda 表达式。

2.3 多线程编程

在多线程编程中,我们需要启动一个新线程来执行某些操作,这时候lambda表达式可以提供一种简洁的方式来定义线程的操作。

#include <iostream>
#include <thread>
 
int main()
{
    int x = 10;
    std::thread t([&x](){ x++;});
    t.join();
    std::cout << x << std::endl;
    return 0;
}

这段代码使用了 C++11 的线程库 std::thread。在 main 函数中,一个 int 类型的变量 x 被赋值为 10。然后创建了一个 std::thread 对象 "t",该对象的构造函数接收了一个 lambda 表达式,在这个 lambda 表达式中,变量 x 的值被自增。

三、总结

在使用lambda时首先要明确它和普通函数一样,有函数地址、参数、返回值等等,使用起来不要有任何负担。第二就是掌握它复杂的使用方法需要多多使用,多多练习,C++新特性的学习有助于我们掌握更好的编程技巧。

到此这篇关于C++11中匿名函数lambda的使用详解的文章就介绍到这了,更多相关C++11匿名函数lambda内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解C++11中的线程锁和条件变量

    详解C++11中的线程锁和条件变量

    C++ 11允许开发者们以标准的、不依赖于平台的方式编写多线程程序。这篇文章概述了标准库对于线程和同步操作机制的支持。这些都是非常重要的知识,希望读者们可以认真看一下
    2021-06-06
  • C++中用new创建二维数组和指针数组实例代码

    C++中用new创建二维数组和指针数组实例代码

    这篇文章主要介绍了C++中用new创建二维数组和指针数组实例代码,非常不错,具有参考借鉴价值,需要的朋友参考下
    2017-03-03
  • Qt 信号自定义槽函数的实现

    Qt 信号自定义槽函数的实现

    Qt中实现自定义信号与槽函数,信号用于发送并触发槽函数,槽函数则是具体的功能实现,本文就详细的介绍一下如何使用,感兴趣的可以了解一下
    2021-11-11
  • OpenCV实现帧差法检测运动目标

    OpenCV实现帧差法检测运动目标

    这篇文章主要为大家详细介绍了OpenCV实现帧差法检测运动目标,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • C语言实现链表与文件存取的示例代码

    C语言实现链表与文件存取的示例代码

    这篇文章主要和大家分享C语言实现链表与文件存取的示例代码,可以实现建立链表,然后把链表数据存储到文件中,然后把文件数据存储到数组中并输出,感兴趣的可以学习一下
    2022-04-04
  • C++读取文本文件中的汉字乱码情况原因及解决

    C++读取文本文件中的汉字乱码情况原因及解决

    本文介绍简体中文Windows操作系统中,C++读取文本文件中的汉字乱码情况原因及解决,文中通过代码和图文给大家介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
    2024-01-01
  • C语言 实现遍历一个文件夹的所有文件

    C语言 实现遍历一个文件夹的所有文件

    这篇文章主要介绍了C语言 实现遍历一个文件夹的所有文件的相关资料,需要的朋友可以参考下
    2017-01-01
  • C++实现基于时序公平的读写锁详解

    C++实现基于时序公平的读写锁详解

    读写锁与普通的互斥锁的区别在于有两种上锁方式:读锁和写锁,不用的用户对同一个读写锁获取读锁是非互斥的,其他情况则是互斥的,本文小编将给大家详细介绍C++实现基于时序公平的读写锁,需要的朋友可以参考下
    2023-10-10
  • VS2017中配置QT5.12.0的图文教程

    VS2017中配置QT5.12.0的图文教程

    本文主要介绍了VS2017中配置QT5.12.0的图文教程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • C++实现通讯录小功能

    C++实现通讯录小功能

    这篇文章主要为大家详细介绍了C++实现通讯录小功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论