C++11 Lambda表达式

Lambda表达式

Lambda表达式是 C++11 时引入的特性。

参考学习资料:

仿函数(functor) 与 Lambda

Lambda表达式又被称作匿名函数,本质是一个特殊的,匿名的类类型。它是一个带有 operator() 的类,即仿函数。
仿函数,就是使一个类的使用看上去像一个函数。其实现就是类中实现一个 operator() ,这个类就有了类似函数的行为,就是一个仿函数类了。

#include <iostream>
class AddNum{
public:
    AddNum(int num) : num_(num) {}

    int operator()(int x) const {
        return num_ + x;
    }

private:
    int num_;
};

int main(){
    // functor
    auto add_num = AddNum(10);
    auto x = add_num(5);
    std::cout << "x:" << x << std::endl;
    return 0;
}
#include <iostream>

int main(){
    // Lambda
    auto add_num = [num_ = 10](int x){ return num_ + x; };
    auto x = add_num(5);
    std::cout << "x:" << x << std::endl;
    return 0;
}

以上两个例子,都是为了实现 num_ + x 的效果。
第一个例子模拟了第二个例子Lambda表达式的实际运行,在使用Lambda表达式时,编译器会自动创建一个仿函数类,只不过这个类的名字我们并不知道。
同时,实现的仿函数 operator() 带有 const 关键字,表示其无法修改成员变量值,这也是下面提到的按值捕获无法修改其值的原因。

Lambda 用法

[ capture ]( params ) opt -> ret { body; };
  • capture 是捕获列表,捕获一定范围内的变量
    • [] 表示不捕获任何变量
    • [=] 表示按值捕获外部作用域中的所有变量,作为副本在匿名函数体内使用,其副本在匿名函数体内是只读的,无法修改值
    • [&] 表示按引用捕获外部作用域中的所有变量,可修改值
  • params 是参数列表,和普通函数的参数列表一样,如果没有参数参数列表可以省略不写
  • opt 是函数选项,不需要可以省略
    • mutable 可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身),mutable是为了突破 const 的限制而设置的
    • exception 指定函数抛出的异常,如抛出整数类型的异常,可以使用 throw ();
  • ret 是返回值类型,在 C++11 中,Lambda表达式的返回值是通过返回值后置语法来定义的。
  • body 是函数体,这部分不能省略,但函数体可以为空。

例 :

  • [=, &var] - 按值捕获外部作用域中所有变量,并按照引用捕获外部变量 var
  • [var] - 按值捕获 var 变量,同时不捕获其他变量
  • [&var] - 按引用捕获 var 变量,同时不捕获其他变量
  • [this] - 捕获当前类中的 this 指针
    • 让 Lambda表达式拥有和当前类成员函数同样的访问权限
    • 如果已经使用了 & 或者 =,默认添加此选项
  •   int x = 0; 
      auto func1 = [=]() { return x++; }; // 报错, 按值捕获外部变量, a是只读的
      auto func2 = [=]() mutable { return x++; }; // 正常运行
    

Lambda表达式的类型

std::function<ret( params )>

其中,std::function 在头文件 #include <functional>
同时,C++11 中可用 auto 关键字代替。

例 实现一个函数功能,输入一个整数,返回这个整数加一。

    int res = [](int x) -> int{
        return ++x;
    }(6);

上述的定义形式,只能够在一处使用,若需在多处使用,可以使用 std::function(或 auto 关键字)或和 std::bind 方法来存储和操作 Lambda表达式:

    auto func1 = [](int x) -> int{
        return ++x;
    };
    auto func2 = std::bind([](int x) -> int{
        return ++x;
    }, std::placeholders::_1);
    // 函数调用
    func1(6); func2(6);
    func1(9); func2(9);

对于没有捕获变量的 Lambda表达式,其类型也可转换为函数指针

    int(*)(int) func = [](int x) -> int{
        return ++x;
    };
    // 函数调用
    func(6);
    func(9);

返回值 ret

在 C++11 中,Lambda表达式返回值的定义也可省略,编译器会自动推导返回值的类型;
但是,不能通过列表初始化自动推导出返回值类型。

    auto func = [](int x){
        return ++x;
    };

    auto func = [](int x){
        return {x+1, x+2};
    };

实例

algorithm 库中的 for_each 方法配合使用:

例 实现一个函数功能,输出指向 vector 中的元素 print_arr

使用普通函数:

#include <iostream>
#include <algorithm>
#include <vector>

// 普通函数
void print_arr(int x){
    std::cout << "x" << x << std::endl;
}

int main()
{

    std::vector<int> arr = {1, 3, 5, 7, 9};
    std::for_each(arr.begin(), arr.end(), print_arr);
    return 0;
}

使用仿函数:

#include <iostream>
#include <algorithm>
#include <vector>

// 仿函数
struct printArr
{
    void operator()(int x){
        std::cout << "x" << x << std::endl;
    }
} print_arr;

int main()
{

    std::vector<int> arr = {1, 3, 5, 7, 9};
    std::for_each(arr.begin(), arr.end(), print_arr);
    return 0;
}

使用 Lambda表达式:

#include <iostream>
#include <algorithm>
#include <vector>

int main()
{

    std::vector<int> arr = {1, 3, 5, 7, 9};
    // Lambda表达式
    std::for_each(arr.begin(), arr.end(), [](int x){ 
        std::cout << "x" << x << std::endl; 
    });
    return 0;
}

对比可见,使用 Lambda表达式,便捷的省去函数的声明和定义。

posted @ 2023-01-07 15:30  Champrin  阅读(55)  评论(0编辑  收藏  举报