c++11 lambda表达式

本文深入探讨C++11中的Lambda表达式,包括其基本构成、如何捕获外部变量、与仿函数的区别及类型转换等内容,并通过示例代码展示了Lambda的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

c++11 lambda表达式

 

lambda 表达式(lambda expression)是一个匿名函数,lambda表达式基于数学中的 λ 演算得名。

C++11中的lambda表达式用于定义并创建匿名的函数对象,以简化编程工作。

lambda表达式的基本构成:

 

函数对象参数

[],标识一个lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义lambda为止时lambda所在作用范围内可见的局部变量(包括lambda所在类的this)。函数对象参数有以下形式:

n  空。没有使用任何函数对象参数。

n  =。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。

n  &。函数体内可以使用lambda所在作用范围内所有可见的局部变量(包括lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。

n  this。函数体内可以使用lambda所在类中的成员变量。

n  a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。

n  &a。将a按引用进行传递。

n  a, &b。将a按值进行传递,b按引用进行传递。

n  =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。

n  &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。

操作符重载函数参数

标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。

可修改标示符

mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。

错误抛出标示符

exception声明,这部分也可以省略。exception声明用于指定函数抛出的异常,如抛出整数类型的异常,可以使用throw(int)

函数返回值

->返回值类型,标识函数返回值的类型,当返回值为void,或者函数体中只有一处return的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。

是函数体

       {},标识函数的实现,这部分不能省略,但函数体可以为空。

 

 

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>


class Test
{
public:
    int i;

    void func(int x, int y)
    {
        auto x1 = []{ return i; };          //err, 没有捕获外部变量
        auto x2 = [=]{ return i+x+y; };     //ok, 值传递方式捕获所有外部变量
        auto x3 = [=]{ return i+x+y; };     //ok, 引用传递方式捕获所有外部变量
        auto x4 = [this]{ return i; };      //ok, 捕获this指针
        auto x5 = [this]{ return i+x+y; };  //err, 没有捕获x, y
        auto x6 = [this, x, y]{ return i+x+y; };//ok, 捕获this指针, x, y
        auto x9 = [this]{ return i++; };        //ok, 捕获this指针, 并修改成员的值
    }
};


void mytest()
{
    int a = 0, b = 1;
    auto f1 = []{ return a; };      //err, 没有捕获外部变量
    auto f2 = [=]{ return a; };     //ok, 值传递方式捕获所有外部变量
    auto f3 = [=]{ return a++; };   //err, a是以赋值方式捕获的,无法修改
    auto f4 = [=]() mutable { return a++; };   //ok, 加上mutable修饰符后,可以修改按值传递进来的拷贝
    auto f5 = [&]{ return a++; };               //ok, 引用传递方式捕获所有外部变量, 并对a执行自加运算
    auto f6 = [a]{ return a+b; };               //err, 没有捕获变量b
    auto f9 = [a,&b]{ return a+(b++); };        //ok, 捕获a, &b
    auto f8 = [=,&b]{ return a+(b++); };        //ok, 捕获所有外部变量,&b

    // 值传递和引用传递区别
    int j = 12;
    auto by_val_lambda = [=] { return j + 1;};
    auto by_ref_lambda = [&] { return j + 1;};
    std::cout << "by_val_lambda: " << by_val_lambda() << std::endl;
    std::cout << "by_ref_lambda: " << by_ref_lambda() << std::endl;
    std::cout << "by_val_lambda: " << by_val_lambda() << std::endl;
    std::cout << "by_ref_lambda: " << by_ref_lambda() << std::endl;

    j++;
    std::cout << "by_val_lambda: " << by_val_lambda() << std::endl;
    std::cout << "by_ref_lambda: " << by_ref_lambda() << std::endl;

    /*
    运行结果:
        by_val_lambda: 13
        by_ref_lambda: 13
        by_val_lambda: 13 // 第3次调用结果还是13,原因是由于by_val_lambda中,j被视为了一个常量,一旦初始化后不会再改变。
        by_ref_lambda: 14
    */


    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

 

lambda与仿函数

通过例子,我们看到,仿函数以round初始化类,而lambda函数也捕获了round变量,其它的,如果在参数传递上,两者保持一致。

除去在语法层面上的不同,lambda和仿函数有着相同的内涵——都可以捕获一些变量作为初始化状态,并接受参数进行运行。

而事实上,仿函数是编译器实现lambda的一种方式,通过编译器都是把lambda表达式转化为一个仿函数对象。因此,在C++11中,lambda可以视为仿函数的一种等价形式。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>

class MyFunctor
{
public:
    MyFunctor(int tmp) : round(tmp) {}
    int operator()(int tmp) { return tmp + round; }

private:
    int round;
};


void mytest()
{

    //仿函数
    int round = 2;
    MyFunctor f1(round);//调用构造函数
    std::cout << "result1 = " << f1(1) << std::endl; //operator()(int tmp)

    //lambda表达式
    auto f2 = [=](int tmp) -> int { return tmp + round; };
    std::cout << "result2 = " << f2(1) << std::endl;

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

 

lambda类型

lambda表达式的类型在C++11中被称为“闭包类型”,每一个lambda表达式则会产生一个临时对象(右值)。因此,严格地将,lambda函数并非函数指针。

不过C++11标准却允许lambda表达式向函数指针的转换,但提前是lambda函数没有捕获任何变量,且函数指针所示的函数原型,必须跟lambda函数函数有着相同的调用方式。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>


void mytest()
{
    //使用std::function和std::bind来存储和操作lambda表达式
    std::function<int(int)> f1 = [](int a) { return a; };
    std::function<int()> f2 = std::bind([](int a){ return a; }, 123);
    std::cout << "f1 = " << f1(123) << std::endl;
    std::cout << "f2 = " << f2() << std::endl;

    auto f3 = [](int x, int y)->int{ return x + y; }; //lambda表达式,没有捕获任何外部变量
    typedef int (*PF1)(int x, int y);   //函数指针类型
    typedef int (*PF2)(int x);

    PF1 p1;     //函数指针变量
    p1 = f3;    //ok, lambda表达式向函数指针的转换
    std::cout << "p1 = " << p1(3, 4) << std::endl;

    PF2 p2;
    //p2 = f3;     //err, 编译失败,参数必须一致

    decltype(f3) p3 = f3;   // 需通过decltype获得lambda的类型
    // decltype(f3) p4 = p1;   // err 编译失败,函数指针无法转换为lambda

    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

 

lambda优势

lambda表达式的价值在于,就地封装短小的功能闭包,可以及其方便地表达出我们希望执行的具体操作,并让上下文结合更加紧密。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <vector>
#include <map>
#include <algorithm> // std::for_each

std::vector<int> nums;
std::vector<int> largeNums;

class LNums
{
public:
    LNums(int u): ubound(u) {}
    void operator ()(int i) const
    {
        if (i > ubound)
        {
            largeNums.push_back(i);
        }
    }

private:
    int ubound;
};

void mytest()
{
    //初始化数据
    for(auto i = 0; i < 10; ++i)
    {
        nums.push_back(i);
    }
    int ubound = 5;

    //1、传统的for循环
    for (auto itr = nums.begin(); itr != nums.end(); ++itr)
    {
        if (*itr > ubound)
        {
            largeNums.push_back(*itr);
        }
    }

    //2、使用仿函数
    std::for_each(nums.begin(), nums.end(), LNums(ubound));

    //3、使用lambda函数和算法for_each
    std::for_each(nums.begin(), nums.end(), [=](int i)
    {
        if (i > ubound)
        {
            largeNums.push_back(i);
        }
    }
    );

    //4、遍历元素
    for_each(largeNums.begin(), largeNums.end(), [=](int i)
    {
        std::cout << i << ", ";
    }
    );
    std::cout << std::endl;


    return;
}

int main()
{
    mytest();

    system("pause");
    return 0;
}

 

### C++11 Lambda表达式使用教程 #### 定义与基本语法 C++11引入了lambda表达式这一强大特性,使得开发者能够更加便捷地定义和创建匿名函数。这种新的功能不仅简化了代码编写过程,还提升了程序的功能性和执行效率[^1]。 Lambda表达式的通用形式如下: ```cpp [capture](parameters)->return_type { body } ``` 其中`capture`表示捕获列表,用于指定如何访问外部作用域中的变量;`parameters`代表参数列表;`->return_type`可选部分用来显式声明返回类型;最后是函数体`body`[^2]。 #### 捕获列表详解 捕获列表允许lambda表达式获取其所在环境内的局部变量或全局变量。常见的几种方式包括但不限于按值传递(`[var]`)、按引用传递(` [&var] `),以及默认全部按值/引用捕获(` [= ] / [&] `)[^3]。 ##### 示例:按值捕获 当采用按值的方式捕获时,意味着会复制一份原始数据供内部逻辑操作而不影响原值。 ```cpp #include <iostream> using namespace std; int main(){ int value = 42; auto func_by_value = [value]() { cout << "Value captured by copy: " << value << endl; }; ++value; // 修改外界的value func_by_value(); // 输出的是未修改前的数值 } ``` ##### 示例:按引用捕获 相反地,如果选择了按引用的形式,则任何对于被捕获变量的操作都会直接影响到实际存在的那个实例。 ```cpp #include <iostream> using namespace std; int main(){ int ref_val = 88; auto modify_ref = [&ref_val]() mutable{ ++ref_val; cout << "Modified reference-captured variable to : " << ref_val << endl; }; modify_ref(); } ``` #### 实际应用场景举例 考虑这样一个场景——我们需要交换两个整型数的位置而无需借助额外的空间开销。此时可以巧妙运用带有适当捕获机制的lambda表达式来完成任务[^5]。 ```cpp void swapTwoNumbers(int &num1, int &num2){ auto swapper = [&](int &a, int &b){ int temp=a;a=b;b=temp; }; swapper(num1,num2); } // 或者更为简洁的做法直接在lambda体内处理 auto direct_swap=[&](int &x,int &y){swap(x,y);} direct_swap(a,b); ``` #### 关于赋值行为的重要注意事项 值得注意的一点在于,尽管可以通过拷贝构造的方式来生成另一个具有相同行为的新对象,但是不同lambda表达式之间不允许互相赋值,即便它们看起来拥有完全一致的结构和语义[^4]。 ```cpp auto hello_world_1=[](){std::cout<<"Hello World"<<std::endl;}; auto hello_world_2=[](){std::cout<<"Hello World"<<std::endl;}; // 下面这行会导致编译错误 //hello_world_1=hello_world_2; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值