C++ Lambda表达式

本文深入解析C++中的Lambda表达式,涵盖其语法、使用场景及特殊行为,包括按值与按引用捕获的区别,mutable关键字的作用,以及如何正确使用Lambda表达式。

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

##简介 Lambda 表达式(lambda expression)是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。

##Lambda的语法 [捕捉块] (参数) mutable或exception声明 -> 返回值类型 {函数体}

Lambda主要分为五个部分:[捕捉块]、(参数)、mutable或exception声明、->返回值类型、{函数体}。

**捕捉块: **

指定捕捉所在作用域中的变量,并供给lambda主体使用。

捕捉列表由多个捕捉项组成,并以逗号分隔。捕捉列表有以下几种形式:

  • [ ] 不捕捉;
  • [var]表示值传递方式捕捉变量var;
  • [=]表示值传递方式捕捉所有作用域的变量(包括this);
  • [&var]表示引用传递捕捉变量var;
  • [&]表示引用传递方式捕捉所有作用域的变量(包括this);
  • [this]表示值传递方式捕捉当前的this指针。

所在作用域,也就是包含Lambda函数的语句块,说通俗点就是包含Lambda的“{}”代码块。上面的捕捉列表还可以进行组合, 例如:

  • [=,&a,&b]表示以引用传递的方式捕捉变量a和b,以值传递方式捕捉其它所有变量;
  • [&,a,this]表示以值传递的方式捕捉变量a和this,引用传递方式捕捉其它所有变量。

不过值得注意的是, 捕获列表没有先后顺序;捕获列表中的变量不能出现重复申。下面一些例子就是典型的重复,会导致编译时期的错误。 例如:

  • [=,a]这里已经以值传递方式捕捉了所有变量,但是重复捕捉a了,会报错的;
  • [&,&this]这里&已经以引用传递方式捕捉了所有变量,再捕捉this也是一种重复。

参数: (可选)lambda表达式使用的参数列表。

只有在不使用任何参数,并且没有自定mutable、一个exception_specification 和一个return_type的情况下可以忽略该列表;

参数可以通过按值(如:(a,b))和按引用(如:(&a, &b))两种方式进行传递。

参数列表和普通函数的参数列表类似,区别如下:

  • 参数不能有默认值。
  • 不允许变长参数列表。
  • 不允许未命名的参数。

mutable:(可选)

如果所在作用域的变量是通过值捕捉到,那么lambda表达式主体中可以使用这些变量的副本。这些副本默认标记为const,因此lambda表达式的主体不能修改这些副本的值。如果lambda表达式标记为mutable,那么这些副本则不是const,因此主体可以修改这些本地副本。

exception_specification:(可选)

用于指定lambda可以抛出的异常。

return_type:(可选)返回值的类型。

如果忽略了return_type,那么编译器会根据以下原则判断返回类型:

  • 如果lambda表达式主体的形式为{return expression;}那么表达式return_type的类型为expression的类型。
  • 其他情况下的return_type为void。

{函数体}

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

##使用

注意:尾部的(),这对括号使得这个lambda表达式立即执行:

    string result = [](const string & str) -> string { return "Hello from " + str; }("second Lambda");  
    cout << "Result: " << result << endl;  

输出如下:

    Result: Hello from second Lambda 

还可以保存lambda表达式的指针,并且通过函数指针执行这个lambda表达式。使用C++11的auto关键字可以轻松地做到这一点:

    auto fn = [](const string& str) {return "hello from " + str; };  
    cout << fn("call 1") << endl;  
    cout << fn("call 2") << endl;  

输出如下:

    Hello from call 1  
    Hello from call 2  

##关于Lambda那些奇葩的东西 看以下一段代码:

#include<iostream>         
using namespace std;       
                           
int main()                 
{                          
    int j = 10;            
    auto by_val_lambda = [=]{ return j + 1; };
    auto by_ref_lambda = [&]{ return j + 1; };
    cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
                           
    ++j;                   
    cout<<"by_val_lambda: "<<by_val_lambda()<<endl;
    cout<<"by_ref_lambda: "<<by_ref_lambda()<<endl;
                           
    return 0;              
}

程序输出结果如下:

by_val_lambda: 11
by_ref_lambda: 11
by_val_lambda: 11
by_ref_lambda: 12

你想到了么???那这又是为什么呢?为什么第三个输出不是12呢?

在by_val_lambda中,j被视为一个常量,一旦初始化后不会再改变(可以认为之后只是一个跟父作用域中j同名的常量),而在by_ref_lambda中,j仍然在使用父作用域中的值。所以,在使用Lambda函数的时候,如果需要捕捉的值成为Lambda函数的常量,我们通常会使用按值传递的方式捕捉;相反的,如果需要捕捉的值成成为Lambda函数运行时的变量,则应该采用按引用方式进行捕捉。

再来一段更晕的代码:

#include<iostream>                  
using namespace std;                
                                    
int main()                          
{                                   
    int val = 0;                                    
    // auto const_val_lambda = [=](){ val = 3; }; wrong!!!
                                    
    auto mutable_val_lambda = [=]() mutable{ val = 3; };
    mutable_val_lambda();           
    cout<<val<<endl; // 0
                                    
    auto const_ref_lambda = [&]() { val = 4; };
    const_ref_lambda();             
    cout<<val<<endl; // 4
                                    
    auto mutable_ref_lambda = [&]() mutable{ val = 5; };
    mutable_ref_lambda();           
    cout<<val<<endl; // 5
                                    
    return 0;      
}

这段代码主要是用来理解Lambda表达式中的mutable关键字的。默认情况下,Lambda函数总是一个const函数,mutable可以取消其常量性。按照规定,一个const的成员函数是不能在函数体内修改非静态成员变量的值。例如上面的Lambda表达式可以看成以下仿函数代码:

class const_val_lambda
{
public:
    const_val_lambda(int v) : val(v) {}
    void operator()() const { val = 3; } // 常量成员函数

private:
    int val;
};

对于const的成员函数,修改非静态的成员变量,所以就出错了。而对于引用的传递方式,并不会改变引用本身,而只会改变引用的值,因此就不会报错了。

转载于:https://my.oschina.net/TemetNosce/blog/862674

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值