「C++11」lambda 表达式的基础使用

注意!

本文于 2019 年 12 月 24 日 起被 放弃维护,您可能会遇到 图片缺失事实错误措辞不当内容过时 等问题,敬请谅解

这里就用一个关于 C++11 的简单的问题作为本篇的开头: 下面哪一行代码会CE?

1
2
3
4
5
6
int main () {
[]{};
[](){};
[]{}();
[](){}();
}

在解决上面那个问题前, 我们先来学习 lambda 表达式相关知识

lambda 表达式

Lambda: Constructs a closure: an unnamed function object capable of capturing variables in scope.

Lambda 表达式 是一个能够捕获作用域中变量的 未命名 函数对象。

首先给出函数原型:

1
[ capture ] ( params ) mutable exception attribute -> return-type { body }

[ capture ] : 捕捉列表. 是lambda的开始标志

( params ) : 参数列表. 如果不需要进行参数传递时, 可以连同括号一起省略掉

mutable exception attribute : 修饰符, 默认 const, 可以加 mutable. 当填入 mutable必须 要有参数列表

-> return-type : 函数的返回值类型, 下面有详细介绍

{body} : 函数体

现在我们逐一分析开头时提到的语句:

1
[]{};

合法, 一个参数列表为空并省略, 返回 void 的 lambda 表达式

1
[](){};

合法, 一个参数列表为空, 返回 void 的 lambda 表达式

1
[]{}();

合法, []{} 表示一个参数列表为空并省略, 返回 void 的 lambda 表达式, 后面加括号表示调用这个函数

1
[](){}();

合法, []{} 表示一个参数列表为空, 返回 void 的 lambda 表达式, 后面加括号表示调用这个函数

所以开头的几个语句都是正确的qwq

看完几个特殊的, 我们来介绍 [ capture ]

[ capture ]

一般有下面几种方式:

  1. [] 不捕获任何外部变量

    这时候, 使用外部变量就会CE

  2. [=] 以值的形式捕获所有外部变量

    可以使用外部变量, 但是不能更改(也就是 const )

  3. [&] 以引用形式捕获所有外部变量

    可以更改外部变量的值

下面是混合形式:

  1. [x, &y] x 以传值形式捕获,y 以引用形式捕获

  2. [=, &z] z 以引用形式捕获,其余变量以传值形式捕获

  3. [&, x] x 以值的形式捕获,其余变量以引用形式捕获

  4. 对于 [=][&] 的形式,lambda 表达式可以直接使用 this 指针。但是,对于 [] 的形式,如果要使用 this 指针,必须显式传入, 比如:

1
2
3
4
5
6
#include <cstdio>
struct Shq {
void vegetable () { printf("Shq is vegetable\n"); }
void lambda_ () { [this] { this->vegetable(); } (); }
} _;
int main () { _.lambda_(); }

-> return-type

这里的 -> return-type尾置返回类型, 其实在 C++11 中的任意函数都可以这样写, 同时函数名前的类型名就可以变为 auto 了, 比如对于一个常规的函数:

1
2
3
4
int *Sort (int *v, int n) {
std::sort (v + 1, v + n + 1);
return v;
}

就可以写为:

1
2
3
4
auto Sort (int *v, int n) -> int * {
std::sort (v + 1, v + n + 1);
return v;
}

当然, lambda 表达式也是如此

lambda 表达式的返回值

lambda 表达式的返回值是一个函数, 比如对于下列 lambda 表达式的返回值就是 std::function <bool(int, int)>

1
2
3
[] (const int &a, const int &b) { return a < b; };
[] (const int &a, const int &b) -> bool { return a < b; };
[] (const int &a, const int &b) -> bool { return a; };

应用

排序

大概 STL 中的 std::sort(), 函数模型是:

1
2
template< class RandomIt, class Compare >
constexpr void sort( RandomIt first, RandomIt last, Compare comp );

后面的 Compare comp 可以使用 lambda 来代替:

1
2
3
std::sort (v + 1, v + n + 1, [] (const int &a, const int &b) {
return a > b;
});

也可以

1
2
3
4
auto cmp = [] (const int &a, const int &b) {
return a > b;
};
std::sort (v + 1, v + n + 1, cmp);

std::set

set 也可以通过 lambda 表达式来定义相对顺序, 首先是 std::set 的函数原型:

1
2
3
4
5
template<
class Key,
class Compare = std::less<Key>,
class Allocator = std::allocator<Key>
> class set;

第二个参数 Compare 也是可以代替的, 下面是一个不使用 lambda 表达式的方法:

1
2
3
4
struct cmp {
bool operator() (const int& a, const int& b) const { return a < b; }
};
std::set <int, cmp> s;

现在来写一个使用 lambda 表达式的 set, 仿照上面的例子, 我们写出了这样的代码:

1
std::set <int, [] (const int &a, const int &b) { return a > b; } > s;

这样并不是可以的, 而是要是用关键字 (decltype) 来进行获取 cmp 的类型再放入第二位......就像这样:

1
2
3
4
5
6
7
8
#include <bits/stdc++.h>

int main () {
auto cmp = [] (const int &a, const int &b) -> bool {
return a > b;
};
std::set <int, decltype(cmp)> s(cmp);
}

其中 lambda 表达式要是 静态全局无状态 的才能使用 decltype

其中 大部分 STL 容器都可以使用这种方式写比较函数

std::for_each

新版本 C++ 的函数, 大概是用于遍历, 先放上函数原型:

1
2
3
4
5
6
7
8
template< class InputIt, class UnaryFunction >
UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

template< class InputIt, class UnaryFunction >
constexpr UnaryFunction for_each( InputIt first, InputIt last, UnaryFunction f );

template< class ExecutionPolicy, class ForwardIt, class UnaryFunction2 >
void for_each( ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, UnaryFunction2 f );

大概原理是这样的:

1
2
3
4
5
6
7
8
template<class InputIt, class UnaryFunction>
constexpr UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
for (; first != last; ++first) {
f(*first);
}
return f; // implicit move since C++11
}

f 自然就随便填 lambda 了, 比如输出 vector 内容:

1
2
auto print = [](const int& n) { std::cout << " " << n; };
std::for_each(v.begin(), v.end(), print);

当然也可以执行操作:

1
std::for_each(nums.begin(), nums.end(), [] (int &n) { n++; });
坚持原创技术分享,您的支持将鼓励我继续创作!