We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在stl课堂上黄老师的讲授中,在介绍不同C++标准更新的特性时,提到了lambda表达式,而这也是C++11 最重要也最常用的一个特性之一,以下对其进行一些粗浅的介绍
1.概念和用法 lambda 表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda 表达式的语法形式可简单归纳如下: [ capture ] ( params ) opt -> ret { body; }; 其中 capture 是捕获列表,params 是参数表,opt 是函数选项,ret 是返回值类型,body是函数体。
因此,一个完整的 lambda 表达式基本形式是这样:
auto lam = [](int a) -> int { return a + 1; }; std::cout << lam(1) << std::endl; // 输出: 2
可以看到,上面通过一行代码定义了一个小小的功能闭包,用来将输入加 1 并返回。
为了简化编写,C++11 中允许省略 lambda 表达式的返回值定义:
auto f = [](int a){ return a + 1; };
这样编译器就会根据 return 语句自动推导出返回值类型。
需要注意的是,初始化列表不能用于返回值的自动推导:
auto x1 = [](int i){ return i; }; // OK auto x2 = [](){ return { 1, 2 }; }; // error: 无法推导出返回值类型
2.捕获列表 lambda 表达式还可以通过捕获列表捕获一定范围内的变量: [] 不捕获任何变量。 [&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。 [=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。 [=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。 [bar] 按值捕获 bar 变量,同时不捕获其他变量。 [this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。
具体用法如下所示
class A { public: int i_ = 0; void func(int x, int y) { auto x1 = []{ return i_; }; // error,没有捕获外部变量 auto x2 = [=]{ return i_ + x + y; }; auto x3 = [&]{ return i_ + x + y; }; auto x4 = [this]{ return i_; }; auto x5 = [this]{ return i_ + x + y; }; // error,没有捕获x、y auto x6 = [this, x, y]{ return i_ + x + y; }; auto x7 = [this]{ return i_++; }; // 捕获this指针,并修改成员的值 } }; int a = 0, b = 1; auto f1 = []{ return a; }; // error,没有捕获外部变量 auto f2 = [&]{ return a++; }; // 捕获所有外部变量,并对a执行自加运算 auto f3 = [=]{ return a; }; // 捕获所有外部变量,并返回a auto f4 = [=]{ return a++; }; // error,a是以复制方式捕获的,无法修改 auto f5 = [a]{ return a + b; }; // error,没有捕获变量b auto f6 = [a, &b]{ return a + (b++); }; // 捕获a和b的引用,并对b做自加运算 auto f7 = [=, &b]{ return a + (b++); }; // 捕获所有外部变量和b的引用,并对b做自加运算
从上例中可以看到,lambda 表达式的捕获列表精细地控制了 lambda 表达式能够访问的外部变量,以及如何访问这些变量。
3.lambda表达式的类型
lambda 表达式的类型在 C++11 中被称为“闭包类型(Closure Type)”。它是一个特殊的,匿名的非 nunion 的类类型。
因此,我们可以认为它是一个带有 operator() 的类,即仿函数。因此,我们可以使用 std::function来存储和操作 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(),如果能直接被转换为普通的函数指针,那么 lambda 表达式本身的 this 指针就丢失掉了。而没有捕获任何外部变量的 lambda 表达式则不存在这个问题。
这里也可以很自然地解释为何按值捕获无法修改捕获的外部变量。因为按照 C++ 标准,lambda 表达式的 operator() 默认是 const 的。一个 const 成员函数是无法修改成员变量的值的。而 mutable 的作用,就在于取消 operator() 的 const。
4.lambda 表达式代替函数对象的示例。
class CountEven { int& count_; public: CountEven(int& count) : count_(count) {} void operator()(int val) { if (!(val & 1)) // 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 << "The number of even is " << 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 & 1)) // val % 2 == 0 { ++ even_count; } }); std::cout << "The number of even is " << even_count << std::endl;
lambda 表达式的价值在于,就地封装短小的功能闭包,可以极其方便地表达出我们希望执行的具体操作,并让上下文结合得更加紧密。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在stl课堂上黄老师的讲授中,在介绍不同C++标准更新的特性时,提到了lambda表达式,而这也是C++11 最重要也最常用的一个特性之一,以下对其进行一些粗浅的介绍
1.概念和用法
lambda 表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。lambda 表达式的语法形式可简单归纳如下:
[ capture ] ( params ) opt -> ret { body; };
其中 capture 是捕获列表,params 是参数表,opt 是函数选项,ret 是返回值类型,body是函数体。
因此,一个完整的 lambda 表达式基本形式是这样:
可以看到,上面通过一行代码定义了一个小小的功能闭包,用来将输入加 1 并返回。
为了简化编写,C++11 中允许省略 lambda 表达式的返回值定义:
这样编译器就会根据 return 语句自动推导出返回值类型。
需要注意的是,初始化列表不能用于返回值的自动推导:
2.捕获列表
lambda 表达式还可以通过捕获列表捕获一定范围内的变量:
[] 不捕获任何变量。
[&] 捕获外部作用域中所有变量,并作为引用在函数体中使用(按引用捕获)。
[=] 捕获外部作用域中所有变量,并作为副本在函数体中使用(按值捕获)。
[=,&foo] 按值捕获外部作用域中所有变量,并按引用捕获 foo 变量。
[bar] 按值捕获 bar 变量,同时不捕获其他变量。
[this] 捕获当前类中的 this 指针,让 lambda 表达式拥有和当前类成员函数同样的访问权限。
具体用法如下所示
从上例中可以看到,lambda 表达式的捕获列表精细地控制了 lambda 表达式能够访问的外部变量,以及如何访问这些变量。
3.lambda表达式的类型
lambda 表达式的类型在 C++11 中被称为“闭包类型(Closure Type)”。它是一个特殊的,匿名的非 nunion 的类类型。
因此,我们可以认为它是一个带有 operator() 的类,即仿函数。因此,我们可以使用 std::function来存储和操作 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(),如果能直接被转换为普通的函数指针,那么 lambda 表达式本身的 this 指针就丢失掉了。而没有捕获任何外部变量的 lambda 表达式则不存在这个问题。
这里也可以很自然地解释为何按值捕获无法修改捕获的外部变量。因为按照 C++ 标准,lambda 表达式的 operator() 默认是 const 的。一个 const 成员函数是无法修改成员变量的值的。而 mutable 的作用,就在于取消 operator() 的 const。
4.lambda 表达式代替函数对象的示例。
lambda 表达式的价值在于,就地封装短小的功能闭包,可以极其方便地表达出我们希望执行的具体操作,并让上下文结合得更加紧密。
The text was updated successfully, but these errors were encountered: