基本用法

// 语法格式
[capture](params) opt -> ret {body;};

其中 capture 是捕获列表,params 是参数列表,opt 是函数选项,ret 是返回值类型,body 是函数体

  • 捕获列表 [] :捕获一定范围内的变量
  • 参数列表 ():和普通函数的参数列表一样,如果没有参数参数列表可以省略不写
  • opt 选项, 不需要可以省略
    • mutable:可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)
    • exception:指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw();
  • 返回值类型:在 C++11 中,lambda 表达式的返回值是通过返回值后置语法来定义的
  • 函数体:函数的实现,这部分不能省略,但函数体可以为空

捕获列表

  • [] 不捕捉任何变量
  • [&] 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
  • [=] 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)
    • 拷贝的副本在匿名函数体内部是只读的
  • [=, &foo] 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo
  • [bar] 按值捕获 bar 变量, 同时不捕获其他变量
  • [&bar] 按引用捕获 bar 变量, 同时不捕获其他变量
  • [this] 捕获当前类中的 this 指针
    • 这个是在类中定义函数时的捕获,不管 [this], [=], [&] 都可访问成员属性和函数,并且修改成员变量的值
    • 如果是在类外使用 & 或者 = 并且创建了类对象,也会捕捉到,= 是只读状态不可修改成员变量值,& 修改会引起成员变量值改变
// 执行体后面加 () 就是表示函数的调用
class Test {
public:
    void output(int x, int y)
    {
        auto x1 = [=] {return a + x + y; }();             // ok
        auto x2 = [this] {return a; }();                  // ok
        auto x3 = [this] {return a + x + y; }();          // 错误,因为没有捕获 x,y
        auto x4 = [this, x, y] {return a + x + y; }();    // ok
        auto x5 = [this] {return a++; }();                // ok,并且会改变 a 的值
        auto x6 = [x] {return x++; }();                   // 错误,按值传递是只读状态
        auto x6 = [x] mutable {return x++; }();           // ok
    }
    int a = 100;
};
void func(int x, int y) {  
    int b = 9;  
    [b]()mutable {  
        b++;  
        cout << b << endl;  
    }();  
    cout << "b: " << b << endl;  
}
// 这个 b 外部输出为 9,lambda 中输出 10,因为按值传递,加上 mutable 具有改变的权限
// 但是不改变原本的值,只是当作副本的修改

// 调用传的参数可以直接使用 ()
void func(int x, int y) {  
    int b = 9;  
    [](int b)mutable {  
        b++;  
        cout << b << endl;  
    }(b);  
    cout << "b: " << b << endl;  
}

返回值

// 完整的lambda表达式定义
auto f = [](int a) -> int {
    return a + 10;  
};

// 忽略返回值的 lambda 表达式定义
// 这是因为编辑器可以自动类型推导
auto f = [](int a) {
    return a + 10;  
};

// 错误,不能推导出返回值类型
// 因为无法确定准确的类型,这可以是数组,类....
auto f1 = []() {
    return {1, 2};	// 基于列表初始化推导返回值,错误
}

函数本质

  • lambda 表达式的类型在 C++11 中会被看做是一个带 operator() 的类,即仿函数
  • 按照 C++标准,lambda 表达式的 operator() 默认是 const 的,一个 const 成员函数是无法修改成员变量值的
  • mutable 选项的作用就在于取消 operator()const 属性

使用 std::functionstd::bind 来存储和操作 lambda 表达式

// 因为本质是一个仿函数
int main()
{
    // 包装可调用函数
    std::function<int(int)> f1 = [=](int a) {
        return a; 
    };
    // 绑定可调用函数
    std::function<int(int)> f2 = bind([=](int a) {
        return a; 
    }, placeholders::_1);

    // 函数调用
    cout << f1(100) << endl;      // 100
    cout << f2(200) << endl;      // 200
    return 0;
}

对于没有捕获任何变量的 lambda 表达式,可以转换成一个普通的函数指针

using func_ptr = int(*)(int);
// 没有捕获任何外部变量的匿名函数
func_ptr f = [](int a) {
    return a;  
};
// 函数调用
f(10);

// 错误,因为不能捕获外部变量
using func_ptr = int(*)(int);
// 没有捕获任何外部变量的匿名函数
func_ptr f = [=](int a) {
    return a;  
};
// 函数调用
f(10);

说明:本文是在 https://subingwen.cn/ 学习过程中的总结,这个 up 主 B 站讲得很好 !!

只管努力,剩下的交给天意