第 22.11 節

Lambda expression

0瀏覽次數0訪問次數--跳出率--平均停留

What problem does this section solve?

Many places require passing in a small piece of logic: sorting rules, filtering conditions, button callbacks, timer callbacks. The old approach typically has three methods:

  1. Write an ordinary function.
  2. Write a function pointer.
  3. Write a function object, which is a class or struct with operator().

All these approaches work, but small logic gets forced to distant locations, or an extra type has to be written. Lambda expressions allow you to write a small function on the spot where it's used and can capture external variables.

What is this feature?

Lambda is an anonymous callable object. It resembles a function but is essentially a class object generated by the compiler.

The basic structure is:

PartexampleMeaning
capture list[x, &y]Which variables to take from the external scope?
parameter list(int a, int b)When invoking a lambda function, you typically pass parameters that define the input data for the function to process. The exact structure depends on the platform or runtime environment you're using. Here are the most common scenarios:

1. AWS Lambda (or similar serverless platforms)

  • event: A JSON-serializable object that contains the input data for the function. This could be an API Gateway request, S3 event, DynamoDB stream, or custom event.
  • context: An object provided by the runtime containing runtime information (e.g., function name, request ID, time remaining).
  • Example invocation:
    {
      "key1": "value1",
      "key2": "value2",
      "key3": "value3"
    }
    

2. General Programming (e.g., Python, JavaScript)

  • In languages like Python, a lambda is an anonymous function that can take arguments.
  • Example:
    square = lambda x: x ** 2
    result = square(5)  # Pass '5' as the argument
    

3. Cloud Functions (e.g., Google Cloud Functions, Azure Functions)

  • Similar to AWS Lambda, they typically expect an event object and sometimes a context or callback.
  • The event structure is often predefined by the trigger (e.g., HTTP request, Pub/Sub message).

4. Custom/In-house Systems

  • You may define your own invocation protocol. Common patterns include:
    • JSON payload: { "action": "process", "data": {...} }
    • Simple arguments: my_lambda(arg1, arg2)
    • Message queue: Sending a serialized message that the lambda consumes.

Key Tips:

  • Check documentation: The exact parameters depend on how the lambda was deployed and its trigger.
  • Start simple: Often, the event object contains the essential data needed for processing.
  • Use context wisely: The context object (if available) helps with logging, timeouts, and other runtime details.

If you have a specific platform or use case in mind, I can provide more tailored details!| |return value|-> int|Return type, often can be omitted| |Function body|{ return a + b; }|The code that is actually executed|

The complete form can be written as [x](int n) -> int { return x + n; }, and in common cases, the return type can be omitted.

C++ standard version

  • C++11: Basic Lambda.
  • C++14: Generic Lambdas, where parameters can be specified as auto.
  • C++17: constexpr Lambda, [*this] capture.

Lambda is a language feature and does not require extra header files. Only when used in conjunction with tools from libraries such as STL algorithms and std::function do you need to include the corresponding header files.

Capture List Quick Reference

writing methodMeaningApplicable Scenarios
[]Do not capture external variables.Only use parameters or local temporary variables.
[x]Capture by value xSave a copy, suitable for saving callbacks.
[&x]Capture by reference xNeed to modify external variables while ensuring their lifecycle.
[x, &y]Hybrid CaptureClarify which are copies and which are references
[=]captures used variables by value by defaultSmall examples are convenient for learning, but avoid overusing them in actual projects.
[&]Capture used variables by defaultEspecially dangerous in asynchronous or saved callbacks.
[this]Capture the current object pointerObjects must outlive lambdas.
[*this]Capture a copy of the current objectAvailable since C++17, useful for avoiding dangling references

Example code

Example 1: Differences Between the Old Callback Style and Lambda Expressions

Both regular functions and function objects can serve as algorithm conditions, but Lambda is more suitable for writing short local logic.

#include <algorithm>
#include <iostream>
#include <vector>

bool is_even(int n)
{
    return n % 2 == 0;
}

struct GreaterThan
{
    int limit;

    bool operator()(int n) const
    {
        return n > limit;
    }
};

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // vector 是动态数组,元素数量可以在运行时变化。
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6};

    int even_count = std::count_if(numbers.begin(), numbers.end(), is_even);
    std::cout << "even count = " << even_count << "\n";

    int greater_count1 = std::count_if(numbers.begin(), numbers.end(), GreaterThan{3});
    std::cout << "> 3 count (functor) = " << greater_count1 << "\n";

    int limit = 3;
    int greater_count2 = std::count_if(numbers.begin(), numbers.end(),
                                       [limit](int n) {
                                           return n > limit;
                                       });
    std::cout << "> 3 count (lambda) = " << greater_count2 << "\n";

    return 0;
}

Results

even count = 3
> 3 count (functor) = 3
> 3 count (lambda) = 3

count_if 的第三个参数是什么?

先看这一句:

int even_count = std::count_if(numbers.begin(), numbers.end(), is_even);

std::count_if 可以简单理解为:

从指定范围中逐个取出元素,调用你提供的“判断规则”,统计其中返回 true 的元素有多少个。

它的基本形式是:

std::count_if(起点, 终点, 判断规则);

第三个参数就是这个“判断规则”。它并不一定非得是普通函数,只要是可以像函数一样调用的对象就可以,包括普通函数、函数对象和 Lambda。这些东西统称为可调用对象(Callable)

这里第三个参数传入的是:

is_even

is_even 是:

bool is_even(int n)
{
    return n % 2 == 0;
}

count_if 会把 numbers 中的元素一个一个传给它,过程可以近似理解成:

is_even(1);  // false,不计数
is_even(2);  // true,计数 +1
is_even(3);  // false,不计数
is_even(4);  // true,计数 +1
is_even(5);  // false,不计数
is_even(6);  // true,计数 +1

因此最后得到 3

可以把 count_if 的内部工作近似理解成:

int count = 0;

for (每一个元素)
{
    if (判断规则(当前元素))
    {
        ++count;
    }
}

也就是说,count_if 自己负责:

  • 怎么遍历容器;
  • 怎么统计数量。

第三个参数负责告诉它:

什么样的元素才算符合条件?

同一个位置也可以传入函数对象:

std::count_if(numbers.begin(), numbers.end(), GreaterThan{3});

GreaterThan{3} 可以像函数一样被调用,因为结构体定义了:

bool operator()(int n) const
{
    return n > limit;
}

它回答的问题就是:

当前这个 n 是否大于 3

还可以直接传 Lambda:

std::count_if(numbers.begin(), numbers.end(),
              [limit](int n) {
                  return n > limit;
              });

count_if 同样会反复调用这个 Lambda:

lambda(1);  // false
lambda(2);  // false
lambda(3);  // false
lambda(4);  // true
lambda(5);  // true
lambda(6);  // true

所以这三种写法虽然形式不同,本质上都是在给 count_if 提供一个判断规则:

is_even
GreaterThan{3}
[limit](int n) { return n > limit; }

Lambda 的优势在于:如果这个判断规则只在这里使用一次,就可以直接写在算法旁边,不需要额外定义一个函数或结构体。

Example 2: Basic Lambda Syntax, Parameters, and Return Values

#include <iostream>
#include <string>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    auto add = [](int a, int b) {
        return a + b;
    };

    auto describe_score = [](int score) -> std::string {
        if (score >= 60)
        {
            return "pass";
        }
        return "fail";
    };

    std::cout << "add(3, 5) = " << add(3, 5) << "\n";
    std::cout << "score 80 is " << describe_score(80) << "\n";
    std::cout << "score 40 is " << describe_score(40) << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

Results

add(3, 5) = 8
score 80 is pass
score 40 is fail

Example 3: capture by value and capture by reference

Capturing by value saves a copy at the time the lambda is defined; capturing by reference accesses the external variable itself.

#include <iostream>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    int score = 10;

    auto add_by_value = [score](int bonus) {
        return score + bonus;
    };

    auto add_by_ref = [&score](int bonus) {
        score += bonus;
        return score;
    };

    score = 20;

    std::cout << "value capture result = " << add_by_value(5) << "\n";
    std::cout << "ref capture result = " << add_by_ref(5) << "\n";
    std::cout << "score after ref capture = " << score << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

Results

value capture result = 15
ref capture result = 25
score after ref capture = 25

Example 4: mutable allows modification of copies of variables captured by value

Variables captured by value are read-only by default inside a lambda. With mutable, you can modify the copy stored by the lambda itself, but it won't affect the outer variable.

#include <iostream>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    int start = 0;

    auto counter = [start]() mutable {
        ++start;
        return start;
    };

    std::cout << "counter() = " << counter() << "\n";
    std::cout << "counter() = " << counter() << "\n";
    std::cout << "outside start = " << start << "\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

Results

counter() = 1
counter() = 2
outside start = 0

为什么连续调用 counter() 会继续累加?

这里不是因为外部 start 的作用域变大了,而是因为 Lambda 对象 counter 自己保存了一份 start

int start = 0;

auto counter = [start]() mutable {
    ++start;
    return start;
};

[start] 是按值捕获。可以近似理解为编译器生成了这样的对象:

struct Counter
{
    int start;

    int operator()()
    {
        ++start;
        return start;
    }
};

Counter counter{start};

因此程序中实际上有两个不同的 start

  • 外部 start:始终还是 0
  • counter 内部保存的 start:第一次调用变成 1,第二次调用继续变成 2
counter();  // 内部 start:0 -> 1
counter();  // 内部 start:1 -> 2

关键在于:

auto counter = [start]() mutable { ... };

这里只创建了一次 Lambda 对象。后面的两个 counter() 都是在调用同一个对象,不会每次调用时重新捕获一次外部 start

mutable 的作用则是允许修改 Lambda 内部按值捕获的副本,它并不会让外部的 start 一起变化。

Example 5: Lambda with STL Algorithms

Lambda is most commonly paired with STL algorithms for operations like sorting, searching, counting, and transforming, where you can directly write the local logic right at the call site.

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // vector 是动态数组,元素数量可以在运行时变化。
    std::vector<std::string> names = {"Bob", "Alice", "Charlie", "David"};

    std::sort(names.begin(), names.end(),
              [](const std::string& a, const std::string& b) {
                  if (a.size() == b.size())
                  {
                      return a < b;
                  }
                  return a.size() < b.size();
              });

    std::cout << "sort by length: ";
    for (const auto& name : names)
    {
        std::cout << name << " ";
    }
    std::cout << "\n";

    int min_length = 6;
    auto it = std::find_if(names.begin(), names.end(),
                           [min_length](const std::string& name) {
                               return name.size() >= static_cast<std::size_t>(min_length);
                           });

    if (it != names.end())
    {
        std::cout << "first long name = " << *it << "\n";
    }

    return 0;
}

Results

sort by length: Bob Alice David Charlie
first long name = Charlie

示例中使用的 API 和语法

这个示例除了 Lambda,还使用了一些常见的 STL API。

|writing method|简单说明|

|:---|:---| |std::vector<std::string>|动态数组,这里用于保存多个字符串|

|names.begin()|返回指向第一个元素的迭代器|

|names.end()|返回最后一个元素后一个位置的迭代器|

|std::sort(begin, end, rule)|对指定范围排序,第三个参数指定比较规则|

|string.size()|返回字符串长度|

|const std::string&|只读引用字符串,避免额外复制|

|for (const auto& x : container)|范围 for,依次访问容器中的每个元素|

|auto|让编译器自动推导变量类型|

|std::find_if(begin, end, rule)|查找第一个满足条件的元素|

|static_cast<T>(value)|将 value 显式转换为类型 T|

|std::size_t|STL 中常用于表示大小、数量和下标的无符号整数类型|

|*it|取得迭代器 it 当前指向的元素|

其中 begin()end() 经常一起出现:

names.begin(), names.end()

表示整个 names 的遍历范围。需要注意,end() 并不指向最后一个元素,而是最后一个元素后面的结束位置:

Bob    Alice    David    Charlie    [结束位置]
 ↑                                      ↑
begin()                                end()

STL 算法为什么要接收一个“规则函数”?

std::count_ifstd::find_ifstd::sort 有一个共同特点:

算法自己负责“怎么做”,你提供的函数负责“按照什么规则做”。

例如 std::sort 自己知道排序算法应该怎样移动和比较元素,但是它不知道你希望按照数值大小、字符串长度还是其他规则排序。

同样,std::find_if 自己知道怎样从前往后查找,但是它不知道“什么样的元素才算你要找的元素”。

因此这些算法会接收一个可调用对象,并在运行过程中反复调用它。

在示例 1 中已经看到:

std::count_if(begin, end, rule);

它会对每个元素调用:

rule(element);

第三个参数回答的是:

这个元素算不算?

find_ifsort 使用的是同一种设计思想,只是它们向规则函数提出的问题不同。

std::find_if:这个元素是不是我要找的?

基本形式是:

std::find_if(起点, 终点, 判断规则);

示例中:

auto it = std::find_if(names.begin(), names.end(),
                       [min_length](const std::string& name) {
                           return name.size() >= static_cast<std::size_t>(min_length);
                       });

[min_length] 按值捕获最小长度 6。这个 Lambda 接收一个字符串,并判断它的长度是否至少为 6

find_if 会依次调用这个 Lambda,可以近似理解成:

rule("Bob");      // false
rule("Alice");    // false
rule("David");    // false
rule("Charlie");  // true

一旦第一次得到 truefind_if 就停止查找,并返回当前元素的位置。

因此 find_if 的第三个参数回答的是:

当前元素是不是我要找的?

它和 count_if 的规则函数形式很像,通常都是:

bool rule(一个元素);

区别在于算法得到 true 之后做什么:

|算法|规则返回 true 后的行为|

|:---|:---| |count_if|计数加一,然后继续检查后面的元素|

|find_if|已经找到,立即停止并返回当前位置|

std::sorta 应不应该排在 b 前面?

std::sort 稍微不同:

std::sort(起点, 终点, 比较规则);

它的第三个参数一次接收两个元素

[](const std::string& a, const std::string& b) {
    if (a.size() == b.size())
    {
        return a < b;
    }
    return a.size() < b.size();
}

sort 会在排序过程中不断调用:

rule(a, b);

这个函数要回答:

按照当前排序规则,a 是否应该排在 b 前面?

For example:

rule("Bob", "Charlie");

因为:

Bob      长度 3
Charlie  长度 7

最终判断的是:

3 < 7

结果为 true,表示按照当前规则,Bob 应该排在 Charlie 前面。

如果两个字符串长度相同:

if (a.size() == b.size())
{
    return a < b;
}

就再使用字符串的字典顺序决定谁在前面。例如 AliceDavid 长度都是 5,因此比较:

"Alice" < "David"

结果为 true,所以 Alice 排在 David 前面。

三个算法可以这样记:

|API|第三个参数回答什么?|

|:---|:---| |count_if|这个元素算不算?|

|find_if|这个元素是不是我要找的?|

|sort|a 应不应该排在 b 前面?|

这正是 Lambda 很常见的用途:STL 算法负责通用流程,Lambda 负责描述具体规则。

const std::string& and .size()

Lambda 中的:

const std::string& a

可以拆成:

  • std::string:参数是字符串;
  • &:使用引用,不额外复制整个字符串;
  • const:只读取这个字符串,不修改它。

因此 const std::string& 常用于只读地接收 stringvector 等较大的对象。

字符串的:

a.size()

返回字符串长度。例如:

"Bob"       -> 3
"Alice"     -> 5
"Charlie"   -> 7
"David"     -> 5

所以示例的排序结果是:

Bob Alice David Charlie

范围 for

for (const auto& name : names)
{
    std::cout << name << " ";
}

可以直接理解为:

依次取出 names 中的每个元素,并把当前元素命名为 name

这里的:

const auto& name

编译器会根据 names 的元素类型自动推导,近似等价于:

const std::string& name

static_cast<std::size_t>

这里:

static_cast<std::size_t>(min_length)

是显式类型转换,可以简单理解为:

min_length 转换成 std::size_t 类型。

因为 name.size() 返回的是 std::size_t,而:

int min_length = 6;

中的 min_lengthint。转换以后,两边使用适合的同类整数类型进行比较。

std::size_t 常用于表示容器大小、字符串长度和数组下标等非负数量。

it 和迭代器

auto it = std::find_if(...);

find_if 返回的不是字符串本身,而是一个迭代器

迭代器可以暂时理解成类似指针的对象,用来表示容器中的某个位置。

排序后的数据是:

Bob    Alice    David    Charlie
                         ↑
                         it

因此:

*it

取得的就是:

Charlie

如果 find_if 一直没有找到满足条件的元素,它会返回:

names.end()

所以常见写法是:

if (it != names.end())
{
    std::cout << *it << "\n";
}

意思就是:

如果确实找到了元素,再使用它。

整段代码的执行过程

程序首先创建:

std::vector<std::string> names = {
    "Bob",
    "Alice",
    "Charlie",
    "David"
};

然后 std::sort 使用 Lambda 作为排序规则:

  1. 长度不同,短的排前面。
  1. 长度相同,按照字符串字典顺序排列。

所以得到:

Bob Alice David Charlie

接下来:

int min_length = 6;

规定要寻找长度至少为 6 的名字。

std::find_if 会按照当前顺序依次调用 Lambda:

Bob       -> 3 >= 6 -> false
Alice     -> 5 >= 6 -> false
David     -> 5 >= 6 -> false
Charlie   -> 7 >= 6 -> true

第一次得到 true 时停止,因此 it 最终指向 Charlie

*it

得到:

Charlie

最终输出:

sort by length: Bob Alice David Charlie
first long name = Charlie

这个示例体现了 Lambda 非常典型的一种用途:

STL 算法负责通用流程,Lambda 负责描述具体规则。

Example 6: Be mindful of lifecycle capture when saving callbacks

If a lambda is invoked immediately, reference capture usually seems fine; if stored in a container, thread, timer, or asynchronous callback, the lambda might execute after the local variables have gone out of scope. When saving a callback, prefer capturing the necessary data by value.

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

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // std::function 可以保存普通函数、lambda 或函数对象。
    // vector 是动态数组,元素数量可以在运行时变化。
    std::vector<std::function<void()>> callbacks;

    {
        std::string name = "Alice";
        int score = 95;

        auto print_now = [&name, &score]() {
            std::cout << "now: " << name << " " << score << "\n";
        };
        print_now();

        callbacks.push_back([name, score]() {
            std::cout << "saved: " << name << " " << score << "\n";
        });
    }

    for (const auto& callback : callbacks)
    {
        callback();
    }

    return 0;
}

Results

now: Alice 95
saved: Alice 95

This is called immediately, so capture by reference is fine. The lambda saved to callbacks uses capture by value, because name and score are already destroyed after leaving the inner scope.

示例 7:捕获 this 访问当前对象

在类的成员函数中,Lambda 可以使用 [this] 捕获当前对象的指针。Lambda 访问的仍然是原来的对象,因此对象成员之后发生的变化也能看到。

#include <iostream>

class Robot
{
public:
    Robot(int speed) : speed_(speed)
    {
    }

    void demo()
    {
        auto print_speed = [this]() {
            std::cout << "lambda speed = " << speed_ << "\n";
        };

        speed_ = 20;
        print_speed();
    }

private:
    int speed_;
};

int main()
{
    Robot robot(10);
    robot.demo();

    return 0;
}

Results

lambda speed = 20

这里:

[this]() {
    std::cout << speed_;
}

可以近似理解为 Lambda 保存了当前对象的 this 指针,因此:

speed_

实际上访问的是:

this->speed_

创建 Lambda 时 speed_10,之后原对象把 speed_ 修改成 20。因为 [this] 指向的仍然是原对象,所以调用 Lambda 时读到的是 20

需要特别注意:[this] 保存的是对象指针,不是对象副本。如果 Lambda 被保存下来,而原对象已经销毁,再通过这个 Lambda 访问成员就可能产生悬空指针问题。因此保存回调、异步任务和线程回调中使用 [this] 时要特别关注对象生命周期。

示例 8:使用 [*this] 捕获当前对象副本

C++17 开始可以使用 [*this]。它不是保存当前对象的指针,而是在创建 Lambda 时复制一份当前对象。

#include <iostream>

class Robot
{
public:
    Robot(int speed) : speed_(speed)
    {
    }

    void demo()
    {
        auto print_speed = [*this]() {
            std::cout << "lambda copy speed = " << speed_ << "\n";
        };

        speed_ = 20;

        print_speed();
        std::cout << "outside speed = " << speed_ << "\n";
    }

private:
    int speed_;
};

int main()
{
    Robot robot(10);
    robot.demo();

    return 0;
}

Results

lambda copy speed = 10
outside speed = 20

这里 Lambda 创建时:

[*this]

会把当时的 Robot 对象复制一份保存到 Lambda 内部。此时副本中的:

speed_ = 10

之后:

speed_ = 20;

修改的是外面的原对象,不会修改 Lambda 已经保存的对象副本,因此 Lambda 仍然输出 10

可以把两种写法这样对比:

|writing method|Lambda 保存什么|原对象后来修改成员后|

|:---|:---|:---| |[this]|当前对象的指针|Lambda 能看到新的成员值|

|[*this]|当前对象的副本|Lambda 保留捕获时的成员值|

[*this] 可以避免因为原对象销毁而直接留下一个悬空的 this 指针,但它会复制整个对象,因此也要考虑对象是否适合复制以及复制成本。

如果需要在 Lambda 中修改这份对象副本,与普通按值捕获类似,可以再配合 mutable

auto callback = [*this]() mutable {
    ++speed_;
};

这里修改的仍然只是 Lambda 内部保存的对象副本,不会修改外面的原对象。

Key Grammar Explanation

|Here is the translation of the provided Simplified Chinese Markdown fragment into natural American English, following all specified rules.


ExampleKey pointsExplanation
Example 1Comparison of old callback writing methodsRegular functions and function objects can both serve as callbacks, but lambda is better suited for short local logic.
Example 2Parameters and return valuesThe return type is usually inferred, but you need to explicitly specify it when different branches return different types.
Example 3capture list[x] copy, [&x] reference
Example 4mutableModifying the internal copy of the lambda does not affect the external variable.
Example 5STL algorithmssort, find_if, and count_if often work with lambda.
Example 6lifecycleIn save callbacks, asynchronous callbacks, and thread callbacks, do not arbitrarily reference captured local variables.
示例 7[this]捕获当前对象指针,Lambda 访问的是原对象

|示例 8|[*this]|C++17 起捕获当前对象副本,与原对象状态分离|

Common Errors

  1. When using default reference capture in callbacks [&], local variables are still accessed after they are destroyed.
  2. It's thought that by-value captures follow external variable changes. By-value captures save a copy at the time the lambda is defined.
  3. Intending to modify the copy captured by value, but forgot to add mutable.
  4. Using a lambda with captures as a function pointer. Lambdas with captures need to be stored using template parameters, auto variables, or std::function.
  5. Capturing this in an asynchronous scenario, the object is destroyed first. Need to ensure the object's lifetime, or use more explicit methods such as smart pointers and [*this].

使用建议

  • 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。
  • 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。
  • 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。
  • 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。
  • 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

  1. For small, localized logic, prefer lambda.
  2. Try to write capture lists explicitly using [x, &y], and avoid relying on defaults [=] and [&] as much as possible.
  3. Prioritize capturing necessary data by value in callbacks, threads, timers, and asynchronous operations.
  4. For callbacks that are called only once and don't need to be saved, you can pass a lambda directly to the algorithm or function template.
  5. 类成员函数中的回调使用 [this] 时要保证对象生命周期;需要独立保存对象状态时可以考虑 C++17 的 [*this]
  1. When you need to uniformly save different lambdas, use the std::function from the next section.

Summary

  • Lambdas are anonymous callable objects, written as [捕获](参数) { 函数体 }.
  • The capture list determines how the lambda uses external variables.
  • [x] is capture by value, and [&x] is capture by reference.
  • mutable allows modification of the internal copy of values captured by value.
  • [this] 捕获当前对象指针,[*this] 从 C++17 起可以捕获当前对象副本。
  • Lambdas are most commonly used in STL algorithms, callbacks, and asynchronous tasks.
  • The lifecycle is the most error-prone aspect of Lambda, especially when dealing with saved callbacks and async callbacks.
音乐页