[1 使用C++11让程序更简洁] 1.6 lambda表达式

1.6 lambda表达式

来源于函数式编程的概念,优点如下:

1 声明式编程风格,就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或函数对象。

2 简洁。

1.6.1 lambda表达式的概念和基本用法

表达式语法:

[ capture ] ( params ) opt -> ret { body; };

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

举例:

auto f = [](int a) -> int { return a+1; };
// 2
std::cout << f(1) << std::endl;

C++11中允许省略lambda表达式的返回值定义,可自动推导:

auto x1 = [](int i){ return i; };

注意,初始化列表不能用于返回值的推导:

// error
auto x2 = []() { return {1, 2}; };

另外,lambda表达式在没有参数列表时,可以省略参数列表。

auto f1 = []() { return 1; };
auto f2 = [] { return 1; };

捕获列表:

[] 不捕获任何变量

[&] 捕获外部作用域中所有变量,作为引用在函数体内使用

[=] 捕获外部作用域中所有变量,作为副本在函数体内使用

[=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获foo变量

[bar] 按值捕获bar变量,不捕获其他变量

[this] 捕获类中的this指针,目的是可以在lambda中使用当前类的成员函数和变量,如果已经使用了&或=,则默认添加此选项

class A
{
public:
    int i_ = 0;
    
    void func(int x, int y)
    {
        // error,没有捕获i_
        auto x1 = []{ return i_; };
        // ok,捕获所有外部变量
        auto x2 = [=]{ return i_ + x + y; };
        // ok,捕获所有外部变量
        auto x3 = [&]{ return i_ + x + y; };
        // ok,捕获this指针
        auto x4 = [this]{ return i_; };
        // error,没有捕获x,y
        auto x5 = [this]{ return i_ + x + y; };
        // ok,捕获this,x,y
        auto x6 = [this,x,y]{ return i_ + x + y; }; 
        // ok,捕获this,并修改了i_成员的值
        auto x7 = [this]{ return i_++; };
    }
};

容易出错的例子:

int a = 0;
auto f = [=]{ return a; };
a += 1;
// 输出:0
std::cout << f() << std::endl;

捕获时,a的值已经复制到f中了,之后f中存储的a仍然是捕获时的值。如果希望lambda表达式在调用时能够即时访问外部变量,应当使用引用方式捕获。

如果希望修改按值捕获的外部变量,需要显式指明lambda表达式为mutable:

int a = 0;
//error, 修改按值捕获的外部变量
auto f1 = [=]{ return a++; };
//ok, mutable
auto f2 = [=]() mutable { return a++; }

注意,被mutable修饰的lambda表达式就算没有参数也要写明参数列表。

lambda表达式类型在C++11中被称为"闭包类型",可以认为它是一个带有operator()的类,即仿函数。因此,我们可以用std::function和std::bind来操作lambda表达式:

std::function<int(int)> f1 = [](int a){ return a; };
std::function<int(void)> f2 = std::bind([](int a){ return a; }, 123);

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

using func_t = (int)(*)(int);
func_t f = [](int a){ return a };
f(123);

解释下为何按值捕获无法修改捕获的外部变量。因为lambda表达式额operator()默认是const的,一个const成员函数是无法修改成员变量的值的。而mutable的作用是取消operator()的const。

1.6.2 声明式编程风格,简洁

简化了标准库算法的调用,例如在C++11之前,要调用for_each函数将vector中的偶数打印出来:

class CountEven
{
    int& count_;
public:
    CountEven(int& count_): count_(count)
    {}
    void operator()(int val)
    {
        if (val % 2 ==0)
        {
            ++count_;
        }
    }
};

std::vector<int> v = { 1, 2, 3, 4, 5, 6 };
int even_count = 0;
for_each(v.begin(), v.end(), CountEven(even_count));
std::cout << even_count << std::endl;

使用lambda后:

std::vector<int> v = { 1, 2, 3, 4, 5, 6 };
int even_count = 0;
for_each(v.begin(), v.end(), [&even_count](int val)
{
    if (val % 2 ==0)
    {
        ++even_count;
    }
});
std::cout << even_count << std::endl;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值