【C++ 学习 ㉛】- 详解 C++11 的 lambda 表达式

目录

一、语法格式

二、函数对象和 lambda 表达式


 


一、语法格式

定义一个 lambda 表达式(lambda 函数)的语法格式如下:

[capture-list] (parameters) mutable noexcept/throw() -> return-type { statements };
即
[捕获列表] (参数列表) 可变规则 异常说明 -> 返回类型 { 函数体 };

其中各部分的含义分别为:

  1. [捕获列表]:[] 是 lambda 引出符,编译器根据该引出符判断接下来的代码是否为 lambda 函数,所以其不能被省略。捕获列表能够捕获外部变量(即和当前 lambda 函数位于同一作用域内的所有局部变量)以供 lambda 函数使用

    捕获列表可由多个捕获项组成,并以逗号分割。捕获列表有以下几种形式

    [捕获列表] 说明
    [] 不捕获任何外部变量
    [=] 以值传递的方式捕获所有外部变量(包括 this)
    [&] 以引用传递的方式捕获所有外部变量(包括 this)
    [val1, val2, ...] 以值传递的方式捕获 val1、val2 等指定的外部变量
    [&val1, &val2, ...] 以引用传递的方式捕获 val1、val2 等指定的外部变量
    [=, &val1, ...] 以引用传递的方式捕获 val1 等指定的外部变量,以值传递的方式捕获其他所有外部变量
    [&, val1, ...] 以值传递的方式捕获 val1 等指定的外部变量,以引用传递的方式捕获其他所有外部变量
    [this] 以值传递的方式捕获当前的 this 指针

    注意:捕获列表不允许外部变量重复传递,例如 [=, val1]、[&、&val1]

  2. (参数列表):和普通函数不同的是,如果不需要传递参数,可以连同 () 一起省略

  3. 可变规则:mutable 关键字可以省略,如果要使用,则之前的 () 将不能省略(参数个数可以为 0)。默认情况下,以值传递方式捕获的外部变量,不允许在 lambda 函数内部修改它们的值(可以理解为这部分变量都是 const 变量),如果想修改它们,就必须使用 mutable 关键字

    注意:修改以值传递方式捕获的外部变量,实则修改的是外部变量的拷贝,而不是真正的外部变量

  4. 异常说明:可以省略,如果要使用,则之前的 () 将不能省略(参数个数可以为 0)。默认情况下,lambda 函数的函数体中可以抛出任何类型的异常,标注 noexcept 关键字,则表示函数体内不会抛出任何异常;使用 throw() 则可以指定 lambda 函数内部可以抛出的异常类型

  5. -> 返回类型:返回类型为 void,可以省略;返回类型明确的情况下,也可以省略,由编译器对返回类型进行推导

  6. { 函数体 }:函数体内部除了可以使用指定传递进来的参数,还可以使用所有捕获的外部变量以及全局范围内的所有全局变量

示例一

#include 
using namespace std;
​
class A
{
public:
    A(int x = 0) : _i(x) { }
​
    void sayHello() const { cout << "Hello" << endl; }
​
    void test()
    {
        // 以值传递的方式捕获当前的 this 指针
        auto func = [this] {
            this->_i = 10;
            cout << this->_i << endl;
            this->sayHello();
            // 函数体中 this 可以省略
        };
        func();
    }
private:
    int _i;
};
​
int main()
{
    // 最简单的 lambda 表达式,该表达式没有任何意义
    [] {};
​
    A a;
    a.test();
    // 10
    // Hello
    return 0;
}

示例二

#include 
#include 
#include 
using namespace std;
​
// 商品类
struct Goods
{
    string _name;  // 名字
    double _price;  // 价格
    int _eval;  // 评价
​
    Goods(const char* str, double price, int eval)
        : _name(str), _price(price), _eval(eval)
    { }
};
​
struct CmpByPriceLess
{
    bool operator()(const Goods& lhs, const Goods& rhs)
    {
        return lhs._price < rhs._price;
    }
};
​
struct CmpByPriceGreater
{
    bool operator()(const Goods& lhs, const Goods& rhs)
    {
        return lhs._price > rhs._price;
    }
};
​
int main()
{
    vector v = {
        { "苹果", 2.1, 5 },
        { "香蕉", 3.0, 4 },
        { "橙子", 2.2, 3 },
        { "菠萝", 1.5, 4}
    };
​
    // 按价格排序方法一:
    sort(v.begin(), v.end(), CmpByPriceLess());  // 升序
    sort(v.begin(), v.end(), CmpByPriceGreater());  // 降序
​
    // 按价格排序方法二:
    sort(v.begin(), v.end(), [](const Goods& lhs, const Goods& rhs) {
        return lhs._price < rhs._price; });  // 升序
    sort(v.begin(), v.end(), [](const Goods& lhs, const Goods& rhs) {
        return lhs._price > rhs._price; });  // 降序
    return 0;
}


二、函数对象和 lambda 表达式

// 利率类
class Rate
{
public:
    Rate(double rate) : _rate(rate) { }
​
    double operator()(double money, int year)
    {
        return money * _rate * year;  // 利息 = 本金 * 利率 * 存期
    }
private:
    double _rate;
};
​
int main()
{
    double rate = 0.49;
    // 函数对象
    Rate r1(rate);  
    r1(1000, 2);
​
    // lambda 表达式
    auto r2 = [rate](double money, int year) {
        return money * rate * year;
    }; 
    r2(1000, 2);
    return 0;
}

【C++ 学习 ㉛】- 详解 C++11 的 lambda 表达式_第1张图片

所以实际上在底层中,编译器对一个 lambda 表达式的处理方式,完全就是按照函数对象的方式来处理的,即如果定义了一个 lambda 表达式,编译器会自动生成一个类,该类中重载了 operator()

注意:lambda 表达式即使看起来是一样,但它们的类型实际上是不相同的

#include 
using namespace std;
​
int main()
{
    auto add1 = [](int x, int y) { return x + y; };
    auto add2 = [](int x, int y) { return x + y; };
    cout << typeid(add1).name() << endl;
    // class 
    cout << typeid(add2).name() << endl;
    // class 
    return 0;
}

你可能感兴趣的:(C++,c++,学习,开发语言)