C++ 11 lambda表达式

Posted Overboom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 11 lambda表达式相关的知识,希望对你有一定的参考价值。

0. lambda表达式特性

lambda 表达式是 C++11 最重要也是最常用的特性之一,这是现代编程语言的一个特点,lambda 表达式有如下的一些优点:

  • 声明式的编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或函数对象
  • 简洁:避免了代码膨胀和功能功能分散,使得开发更加高效
  • 在需要的时间和地点实现功能闭包,使得程序更加灵活

1. lambda表达式语法

lambda表达式定义了一个匿名函数,并且可以补货一定范围内的变量。一般语法形式如下:

[capture](parameters)options ->return_type{statement}
  • capture是捕获列表,捕捉列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数。捕捉列表能够捕捉上下文中的变量以供Lambda函数使用;
  • parameters是参数列表,与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略;
  • opt是函数选项,不需要可以省略。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。
    - mutable: 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    - exception: 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
  • return_type是返回值类型,追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导;
  • statement是函数体,内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

2. 捕获列表

lambda 表达式的捕获列表可以捕获一定范围内的变量,具体使用方式如下:

  • [] : 不捕捉任何变量

  • [&] : 捕获外部作用域中所有变量,并作为引用在函数体内使用 (按引用捕获)

  • [=] : 捕获外部作用域中所有变量,并作为副本在函数体内使用 (按值捕获)

    拷贝的副本在匿名函数体内部是只读的

  • [=, &foo] : 按值捕获外部作用域中所有变量,并按照引用捕获外部变量 foo

  • [bar] : 按值捕获 bar 变量,同时不捕获其他变量

  • [&bar] : 按引用捕获 bar 变量,同时不捕获其他变量

  • [this] : 捕获当前类中的 this 指针

    让 lambda 表达式拥有和当前类成员函数同样的访问权限
    如果已经使用了 & 或者 =, 默认添加此选项
    下面通过具体的例子,看捕获列表的用法:

#include <iostream>
#include <functional>
using namespace std;

class Test
{
public:
    void output(int x, int y)
    {
        auto x1 = [] {return m_number; };                      // error
        auto x2 = [=] {return m_number + x + y; };             // ok
        auto x3 = [&] {return m_number + x + y; };             // ok
        auto x4 = [this] {return m_number; };                  // ok
        auto x5 = [this] {return m_number + x + y; };          // error
        auto x6 = [this, x, y] {return m_number + x + y; };    // ok
        auto x7 = [this] {return m_number++; };                // ok
    }
    int m_number = 100;
};
  • x1:错误,没有捕获外部变量,不能使用类成员 m_number
  • x2:正确,以值拷贝的方式捕获所有外部变量
  • x3:正确,以引用的方式捕获所有外部变量
  • x4:正确,捕获 this 指针,可访问对象内部成员
  • x5:错误,捕获 this 指针,可访问类内部成员,没有捕获到变量 x,y,因此不能访问。
  • x6:正确,捕获 this 指针,x,y
  • x7:正确,捕获 this 指针,并且可以修改对象内部变量的值

3. parameters函数参数

Lambda表达式的参数和普通函数的参数类似,那么这里为什么还要拿出来说一下呢?原因是在Lambda表达式中传递参数还有一些限制,主要有以下几点:

  • 参数列表中不能有默认参数
  • 不支持可变参数
  • 所有参数必须有参数名
    常用举例:
     int m = [](int x) { return [](int y) { return y * 2; }(x)+6; }(5);
        std::cout << "m:" << m << std::endl;              //输出m:16

        std::cout << "n:" << [](int x, int y) { return x + y; }(5, 4) << std::endl;            //输出n:9
        
        auto gFunc = [](int x) -> function<int(int)> { return [=](int y) { return x + y; }; };
        auto lFunc = gFunc(4);
        std::cout << lFunc(5) << std::endl;

        auto hFunc = [](const function<int(int)>& f, int z) { return f(z) + 1; };
        auto a = hFunc(gFunc(7), 8);

        int a = 111, b = 222;
        auto func = [=, &b]()mutable { a = 22; b = 333; std::cout << "a:" << a << " b:" << b << std::endl; };

        func();
        std::cout << "a:" << a << " b:" << b << std::endl;

        a = 333;
        auto func2 = [=, &a] { a = 444; std::cout << "a:" << a << " b:" << b << std::endl; };
        func2();

        auto func3 = [](int x) ->function<int(int)> { return [=](int y) { return x + y; }; };

	std::function<void(int x)> f_display_42 = [](int x) { print_num(x); };
	f_display_42(44);

4. opt函数选项

在Lambda表达式中,如果以传值方式捕获外部变量,则函数体中不能修改该外部变量,否则会引发编译错误。那么有没有办法可以修改值捕获的外部变量呢?这是就需要使用mutable关键字,该关键字用以说明表达式体内的代码可以修改值捕获的变量,示例:

int main()
{
    int a = 123;
    auto f = [a]()mutable { cout << ++a; }; // 不会报错
    cout << a << endl; // 输出:123
    f(); // 输出:124
}

参看链接:
https://blog.csdn.net/u010984552/article/details/53634513
https://subingwen.cn/cpp/lambda/
https://www.cnblogs.com/DswCnblog/p/5629165.html

以上是关于C++ 11 lambda表达式的主要内容,如果未能解决你的问题,请参考以下文章

c++中lambda表达式用法

C++中Lambda表达式浅析

c++ 11 lambda表达式

c++ 11 lambda表达式

c++ 11之 lambda表达式的使用

c++ 11之 lambda表达式的使用