跳转至

嵌入式C++教程——Lambda捕获与性能影响

引言

上一章我们学习了Lambda的基本语法,但你可能心中还有一个疑问:那个捕获列表[...]到底是怎么回事?值捕获和引用捕获有什么区别?会不会影响性能?

这些问题在嵌入式开发中尤为重要——我们既要代码优雅,又要零开销。

一句话总结:捕获决定了Lambda如何访问外部变量,不同的捕获方式有不同的性能和安全性考虑。


捕获方式全解析

Lambda的捕获列表可以按以下方式使用:

捕获方式 语法 说明
空捕获 [] 不捕获任何外部变量
值捕获 [x] 复制变量x的值
引用捕获 [&x] 引用变量x
全值捕获 [=] 值捕获所有外部变量
全引用捕获 [&] 引用捕获所有外部变量
混合捕获 [x, &y] x值捕获,y引用捕获
初始化捕获 [x = expr] C++14,用表达式初始化捕获变量

让我们逐个深入理解。


值捕获:复制一份副本

值捕获会在Lambda对象中存储被捕获变量的副本:

void example_value_capture() {
    int threshold = 100;

    // threshold被复制到Lambda对象中
    auto is_high = [threshold](int value) {
        return value > threshold;  // 使用的是副本
    };

    threshold = 200;  // 修改外部变量,不影响Lambda
    bool result = is_high(150);  // false,因为Lambda里的threshold还是100
}

关键点

  • Lambda创建时复制,之后外部修改不影响
  • Lambda内部修改也不会影响外部
  • 默认是const的,如果要修改需要加mutable
查看完整可编译示例
// Lambda Value Capture
// Demonstrates value capture semantics - the variable is copied into the lambda

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

int main() {
    std::cout << "=== Value Capture Demo ===" << std::endl;

    // Basic value capture
    int threshold = 100;

    auto is_high = [threshold](int value) {
        // threshold is a const copy inside the lambda
        return value > threshold;
    };

    std::cout << "is_high(150) = " << is_high(150) << std::endl;  // true
    std::cout << "is_high(50) = " << is_high(50) << std::endl;    // false

    // Modifying external variable doesn't affect the lambda
    threshold = 200;
    std::cout << "After threshold=200, is_high(150) = " << is_high(150) << std::endl;  // still true (uses original 100)

    // Multiple value captures
    int a = 10, b = 20, c = 30;
    auto sum = [a, b, c]() {
        return a + b + c;  // All are copies
    };
    std::cout << "sum() = " << sum() << std::endl;

    a = 100;
    std::cout << "After a=100, sum() = " << sum() << std::endl;  // Still 60

    // Value capture with complex types
    std::string message = "Hello, ";
    auto greet = [message]() {
        return message + "World!";  // message is copied
    };
    std::cout << "greet() = " << greet() << std::endl;

    message = "Goodbye, ";
    std::cout << "After message change, greet() = " << greet() << std::endl;  // Still "Hello, World!"

    // Embedded scenario: Configuration parameter capture
    std::cout << "\n=== Embedded: PWM Config ===" << std::endl;

    uint32_t base_addr = 0x40000000;
    int frequency = 1000;

    auto set_duty = [base_addr, frequency](int percent) {
        uint32_t period = 1000000 / frequency;  // Safe - frequency won't change
        uint32_t duty = period * percent / 100;
        std::cout << "PWM @ 0x" << std::hex << base_addr << std::dec
                  << ": period=" << period << "us, duty=" << duty << "us (" << percent << "%)" << std::endl;
    };

    set_duty(25);
    set_duty(50);
    set_duty(75);

    frequency = 2000;  // Doesn't affect the lambda
    std::cout << "After frequency=2000, set_duty still uses 1000Hz" << std::endl;
    set_duty(50);

    // Thread-safe counter (each lambda gets its own copy)
    std::cout << "\n=== Thread-Safe Counter ===" << std::endl;

    int counter = 0;
    auto get_lambda = [counter]() mutable {  // See mutable_capture.cpp for details
        counter++;
        return counter;
    };

    // This creates a new lambda with a fresh copy each time
    auto lambda1 = [counter]() mutable {
        counter++;
        return counter;
    };
    counter = 100;
    auto lambda2 = [counter]() mutable {
        counter++;
        return counter;
    };

    std::cout << "lambda1() = " << lambda1() << std::endl;  // 1
    std::cout << "lambda2() = " << lambda2() << std::endl;  // 101

    return 0;
}

mutable关键字

值捕获的变量默认是const,如果要在Lambda内修改,需要mutable

int counter = 0;
// ❌ 编译错误:counter是const
auto lambda1 = [counter]() { counter++; };

// ✅ 加上mutable
auto lambda2 = [counter]() mutable {
    counter++;  // 修改的是Lambda内的副本
    return counter;
};

嵌入式场景:值捕获适合配置参数,确保线程安全:

class UARTDriver {
public:
    void send_break(uint32_t duration_us) {
        // duration_us被捕获,确保发送过程中不会被修改
        auto send = [this, duration_us]() {
            *ctrl_reg |= BREAK_ENABLE;
            delay_microseconds(duration_us);  // 安全的duration值
            *ctrl_reg &= ~BREAK_ENABLE;
        };
        send();
    }
};

引用捕获:共享原始变量

引用捕获让Lambda直接访问外部变量,而不是复制:

void example_ref_capture() {
    int sum = 0;

    // 引用捕获sum
    auto accumulator = [&sum](int value) {
        sum += value;  // 直接修改外部sum
    };

    accumulator(10);
    accumulator(20);
    // sum = 30
}

关键点

  • Lambda持有外部变量的引用,不是副本
  • 外部修改会影响Lambda,反之亦然
  • 需要确保外部变量生命周期比Lambda长
查看完整可编译示例
// Lambda Reference Capture
// Demonstrates reference capture and lifetime considerations

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

int main() {
    std::cout << "=== Reference Capture Demo ===" << std::endl;

    // Basic reference capture
    int sum = 0;

    auto accumulator = [&sum](int value) {
        sum += value;  // Modifies the original sum variable
    };

    accumulator(10);
    accumulator(20);
    accumulator(30);
    std::cout << "After accumulations, sum = " << sum << std::endl;  // 60

    // Reference capture reflects external changes
    int threshold = 100;
    auto is_high = [&threshold](int value) {
        return value > threshold;
    };

    std::cout << "is_high(150) = " << is_high(150) << std::endl;  // true

    threshold = 200;
    std::cout << "After threshold=200, is_high(150) = " << is_high(150) << std::endl;  // false

    // Full reference capture
    int x = 1, y = 2, z = 3;
    auto modify_all = [&]() {
        x *= 2;
        y *= 2;
        z *= 2;
    };
    modify_all();
    std::cout << "After modify_all: x=" << x << ", y=" << y << ", z=" << z << std::endl;

    // Mixed capture: some by value, some by reference
    int multiplier = 2;
    int total = 0;
    auto mixed = [multiplier, &total](int value) {
        total += value * multiplier;  // multiplier is copied, total is referenced
    };
    mixed(5);
    mixed(10);
    std::cout << "After mixed(5, 10), total = " << total << std::endl;  // 30

    // WARNING: Lifetime issues
    std::cout << "\n=== Lifetime Warning ===" << std::endl;

    // BAD: Returning lambda with reference to local variable
    /*
    auto bad_counter() {
        int count = 0;
        return [&count]() { return ++count; };  // Dangling reference!
    }
    */

    // GOOD: Return by value or use safe lifetime
    auto safe_counter() {
        int count = 0;
        return [count]() mutable {  // Copy the value
            return ++count;
        };
    }

    auto counter1 = safe_counter();
    std::cout << "safe_counter() = " << counter1() << std::endl;  // 1
    std::cout << "safe_counter() = " << counter1() << std::endl;  // 2

    // Using class to manage lifetime
    class SafeCounter {
    public:
        std::function<int()> get_lambda() {
            return [this]() {
                return ++count;
            };
        }
    private:
        int count = 0;
    };

    SafeCounter sc;
    auto counter2 = sc.get_lambda();
    std::cout << "SafeCounter lambda = " << counter2() << std::endl;  // 1

    // Embedded scenario: Using reference capture for status
    std::cout << "\n=== Embedded: Status Update ===" << std::endl;

    struct Status {
        bool ready = false;
        int error_code = 0;
    } status;

    auto set_ready = [&status]() {
        status.ready = true;
        status.error_code = 0;
    };

    auto set_error = [&status](int code) {
        status.ready = false;
        status.error_code = code;
    };

    set_ready();
    std::cout << "Status: ready=" << status.ready << ", error=" << status.error_code << std::endl;

    set_error(42);
    std::cout << "Status: ready=" << status.ready << ", error=" << status.error_code << std::endl;

    // Caution with loop variables
    std::cout << "\n=== Loop Capture Caution ===" << std::endl;

    std::vector<std::function<void()>> handlers;

    // BAD: All lambdas reference the same i
    /*
    for (int i = 0; i < 5; ++i) {
        handlers.push_back([&i]() { std::cout << i << " "; });
    }
    // All output 5 (or undefined)
    */

    // GOOD: Capture by value
    for (int i = 0; i < 5; ++i) {
        handlers.push_back([i]() { std::cout << i << " "; });
    }

    std::cout << "Handlers output: ";
    for (auto& h : handlers) {
        h();
    }
    std::cout << std::endl;

    return 0;
}

生命周期陷阱

引用捕获的最大风险是悬垂引用:

// ❌ 危险:返回的Lambda引用了局部变量
auto make_counter() {
    int count = 0;
    return [&count]() { return ++count; };  // count已销毁!
}

// ❌ 危险:存储到函数对象后使用
std::function<void()> store_lambda() {
    int local = 42;
    return [&local]() { /* ... */ };  // local即将销毁
}

安全实践:确保引用捕获的变量生命周期足够长:

class Device {
public:
    void init() {
        // this的生命周期比Lambda长,安全
        auto handler = [this]() {
            this->status = READY;
        };
        register_callback(handler);
    }
private:
    Status status;
};

全捕获:一网打尽

当需要捕获的变量很多时,可以一次性捕获所有:

void example_full_capture() {
    int a = 1, b = 2, c = 3, d = 4;

    // [=] 全部值捕获
    auto lambda1 = [=]() {
        return a + b + c + d;  // a,b,c,d都是副本
    };

    // [&] 全部引用捕获
    auto lambda2 = [&]() {
        a++; b++;  // 直接修改外部变量
    };
}

混合捕获:可以指定某些变量用特殊方式:

void example_mixed_capture() {
    int threshold = 100;
    int count = 0;
    double factor = 1.5;

    // threshold和factor值捕获,count引用捕获
    auto process = [threshold, factor, &count](int value) {
        if (value > threshold) {
            count++;
            return static_cast<int>(value * factor);
        }
        return value;
    };
}

嵌入式建议:避免使用[&]全引用捕获,容易无意捕获不该捕获的变量。


初始化捕获(C++14):更灵活的捕获方式

C++14引入了初始化捕获,允许在捕获时进行任意表达式计算:

void example_init_capture() {
    int base = 10;

    // 捕获base+5的结果,而不是base本身
    auto lambda = [value = base + 5]() {
        return value * 2;  // value是15
    };

    // 捕获移动的类型
    std::unique_ptr<int> ptr = std::make_unique<int>(42);
    auto lambda2 = [p = std::move(ptr)]() {
        return *p;  // p是移动进来的
    };
}

嵌入式场景:捕获计算后的配置:

void configure_timer(int frequency_hz) {
    // 捕获计算好的寄存器值,而不是频率
    auto setup = [prescaler = SystemClock / frequency_hz - 1]() {
        *TIM_PSC = prescaler;
        *TIM_ARR = 999;
    };
    setup();
}

比mutable更清晰

// C++11写法:需要mutable
int x = 0;
auto lambda1 = [x]() mutable {
    x += 1;
    return x;
};

// C++14写法:初始化捕获,语义更清晰
auto lambda2 = [counter = 0]() {
    counter += 1;
    return counter;
};
查看完整可编译示例
// Lambda Init Capture (C++14)
// Demonstrates generalized lambda capture with initialization expressions

#include <iostream>
#include <memory>
#include <string>
#include <vector>

int main() {
    std::cout << "=== Init Capture Demo (C++14) ===" << std::endl;

    // Basic init capture: capture result of expression
    int base = 10;

    auto lambda = [value = base + 5]() {
        return value * 2;  // value is 15
    };

    std::cout << "lambda() = " << lambda() << std::endl;  // 30

    // Capturing computed value
    auto timer_config = [prescaler = 72000000 / 10000 - 1]() {
        std::cout << "Timer prescaler: " << prescaler << std::endl;
        return prescaler;
    };
    timer_config();

    // Move capture with unique_ptr
    std::cout << "\n=== Move Capture ===" << std::endl;

    auto ptr = std::make_unique<int>(42);
    std::cout << "Original ptr value: " << *ptr << std::endl;

    auto lambda_with_ptr = [p = std::move(ptr)]() {
        return *p;
    };
    std::cout << "lambda_with_ptr() = " << lambda_with_ptr() << std::endl;
    // ptr is now nullptr (moved into lambda)

    // More practical example: move a vector
    auto large_data = std::make_unique<std::vector<int>>(1000, 42);

    auto process_data = [data = std::move(large_data)]() {
        std::cout << "Processing " << data->size() << " elements" << std::endl;
        return data->front();
    };
    std::cout << "process_data() = " << process_data() << std::endl;

    // Init capture vs mutable
    std::cout << "\n=== Init Capture vs Mutable ===" << std::endl;

    // C++11 style with mutable
    int x = 0;
    auto lambda_mutable = [x]() mutable {
        x += 1;
        return x;
    };
    std::cout << "lambda_mutable() = " << lambda_mutable() << std::endl;  // 1
    std::cout << "lambda_mutable() = " << lambda_mutable() << std::endl;  // 2

    // C++14 style with init capture - clearer intent
    auto counter = [count = 0]() mutable {
        count += 1;
        return count;
    };
    std::cout << "counter() = " << counter() << std::endl;  // 1
    std::cout << "counter() = " << counter() << std::endl;  // 2

    // Complex init capture
    std::cout << "\n=== Complex Init Capture ===" << std::endl;

    std::string name = "sensor";
    auto [id = 1, label = std::move(name)]() {
        std::cout << "ID: " << id << ", Label: " << label << std::endl;
    }();

    // Capturing by reference with alias
    int value = 100;
    auto ref_alias = [ref = value]() {
        std::cout << "ref = " << ref << std::endl;
    };

    // Embedded: Capture computed register values
    std::cout << "\n=== Embedded: Register Configuration ===" << std::endl;

    int system_clock = 72000000;
    int target_frequency = 1000;

    auto setup_timer = [psc = system_clock / (target_frequency * 1000) - 1,
                        arr = 10000 - 1]() {
        std::cout << "PSC = " << psc << ", ARR = " << arr << std::endl;
        std::cout << "Resulting frequency = "
                  << 72000000 / ((psc + 1) * (arr + 1)) << " Hz" << std::endl;
    };
    setup_timer();

    // Init capture for state machine
    std::cout << "\n=== State Machine Example ===" << std::endl;

    enum State { IDLE, RUNNING, DONE };

    auto state_machine = [state = IDLE, count = 0]() mutable {
        switch (state) {
            case IDLE:
                count = 0;
                state = RUNNING;
                return "Started";
            case RUNNING:
                count++;
                if (count > 2) state = DONE;
                return "Running";
            case DONE:
                return "Done";
        }
        return "Unknown";
    };

    std::cout << state_machine() << std::endl;
    std::cout << state_machine() << std::endl;
    std::cout << state_machine() << std::endl;
    std::cout << state_machine() << std::endl;

    // Capturing const references
    std::cout << "\n=== Const Reference Capture ===" << std::endl;

    const std::string config = "some_config";

    // Capture by const reference (no copy)
    auto use_config = [&cfg = config]() {
        std::cout << "Using config: " << cfg << std::endl;
    };
    use_config();

    // Capture with decltype(auto) for perfect forwarding (C++17/20)
    auto forward_capture = [x = std::move(value)]() {
        std::cout << "Captured value: " << x << std::endl;
    };
    forward_capture();

    return 0;
}

性能影响:到底有没有开销?

这是嵌入式开发者最关心的问题。让我们从汇编层面分析。

值捕获的开销

值捕获本质上是把变量存为Lambda对象的成员变量:

int threshold = 100;
auto lambda = [threshold](int x) { return x > threshold; };

编译器大致生成类似这样的代码:

struct LambdaType {
    int threshold;  // 成员变量存储捕获的值

    bool operator()(int x) const { return x > threshold; }
};

LambdaType lambda{threshold};  // 构造时复制

性能分析

  • Lambda对象大小 = 所有值捕获变量的大小之和
  • 构造时有复制开销
  • 调用时有额外参数(捕获的成员),但内联后无开销

验证:汇编层面的零开销

// 示例代码
int threshold = 100;
auto is_high = [threshold](int x) { return x > threshold; };
int result = is_high(150);

-O2优化下,这会被完全内联为:

; 伪汇编
mov eax, 150
cmp eax, 100
setg al

   ```

**结论**:只要Lambda可内联,值捕获在调用时**零开销**

<details>
<summary>查看完整可编译示例</summary>

```cpp
// Lambda Capture Performance Analysis
// Demonstrates that lambda capture has zero runtime overhead when inlined

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

// Global function for comparison
int add_global(int a, int b) {
    return a + b;
}

// Function with direct call
int direct_call(int (*func)(int, int), int a, int b) {
    return func(a, b);
}

// Lambda with value capture
int test_value_capture() {
    int threshold = 100;
    auto is_high = [threshold](int x) { return x > threshold; };

    int sum = 0;
    for (int i = 0; i < 1000; ++i) {
        if (is_high(i)) {
            sum += i;
        }
    }
    return sum;
}

// Lambda with reference capture
int test_reference_capture() {
    int threshold = 100;
    auto is_high = [&threshold](int x) { return x > threshold; };

    int sum = 0;
    for (int i = 0; i < 1000; ++i) {
        if (is_high(i)) {
            sum += i;
        }
    }
    return sum;
}

// No lambda (inline comparison)
int test_no_lambda() {
    int threshold = 100;
    int sum = 0;
    for (int i = 0; i < 1000; ++i) {
        if (i > threshold) {
            sum += i;
        }
    }
    return sum;
}

// Benchmarking function
template<typename Func>
long long benchmark(Func&& func, int iterations = 10000) {
    auto start = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < iterations; ++i) {
        volatile int result = func();
        (void)result;
    }

    auto end = std::chrono::high_resolution_clock::now();
    return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}

int main() {
    std::cout << "=== Lambda Capture Performance Demo ===" << std::endl;
    std::cout << "Compiled with -O2 optimization" << std::endl;
    std::cout << "Run with -O2 for fair comparison" << std::endl;

    // Warm up
    test_value_capture();
    test_reference_capture();
    test_no_lambda();

    std::cout << "\nBenchmarking 10,000 iterations each..." << std::endl;

    auto time_value = benchmark(test_value_capture);
    auto time_ref = benchmark(test_reference_capture);
    auto time_no_lambda = benchmark(test_no_lambda);

    std::cout << "\nResults:" << std::endl;
    std::cout << "  Value capture:    " << time_value << " us" << std::endl;
    std::cout << "  Reference capture: " << time_ref << " us" << std::endl;
    std::cout << "  No lambda:        " << time_no_lambda << " us" << std::endl;

    std::cout << "\nConclusion: With -O2, all three perform similarly." << std::endl;
    std::cout << "The compiler inlines the lambda, making capture overhead zero." << std::endl;

    // Demonstrate inline optimization
    std::cout << "\n=== Inline Optimization Demo ===" << std::endl;

    int x = 42;
    auto lambda = [x](int y) { return x + y; };

    // With -O2, this is optimized to: result = 42 + 10
    int result = lambda(10);
    std::cout << "lambda(10) with x=42 = " << result << std::endl;
    std::cout << "Compiler likely optimized this to: return 52;" << std::endl;

    // Complex example with STL algorithm
    std::cout << "\n=== STL Algorithm Performance ===" << std::endl;

    std::vector<int> data(100000);
    for (size_t i = 0; i < data.size(); ++i) {
        data[i] = static_cast<int>(i);
    }

    // Lambda with capture
    int threshold = 50000;
    auto start = std::chrono::high_resolution_clock::now();
    auto it = std::find_if(data.begin(), data.end(),
                          [threshold](int v) { return v > threshold; });
    auto end = std::chrono::high_resolution_clock::now();

    if (it != data.end()) {
        auto time = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        std::cout << "Found value > " << threshold << ": " << *it << std::endl;
        std::cout << "Time taken: " << time.count() << " us" << std::endl;
    }

    // Memory overhead discussion
    std::cout << "\n=== Memory Overhead ===" << std::endl;
    std::cout << "Lambda object size = sum of captured variables" << std::endl;
    std::cout << "Empty lambda: " << sizeof([](){}) << " bytes" << std::endl;
    std::cout << "Lambda capturing one int: " << sizeof([x](){} ) << " bytes" << std::endl;
    std::cout << "Lambda capturing two ints: " << sizeof([x, x](){} ) << " bytes" << std::endl;

    return 0;
}


   ```

</details>

### 引用捕获的开销

引用捕获存储的是指针:

```cpp
struct LambdaType {
    int* threshold_ptr;  // 指针成员

    bool operator()(int x) const { return x > *threshold_ptr; }
};

性能分析

  • Lambda对象大小 = 所有引用捕获变量的指针大小之和
  • 调用时需要解引用,可能影响优化
  • 但内联后通常也能优化掉

结论:引用捕获在调用时也有接近零开销(多一层间接访问)。


何时选择哪种捕获方式

选择值捕获的情况

  1. 配置参数:Lambda执行期间参数不应改变
auto send_bytes = [timeout = 1000](const uint8_t* data, size_t len) {
    // timeout在发送过程中不变
};
  1. 小型可复制对象intfloat、简单结构体
auto scale = [factor = 1.5f](int x) { return x * factor; };
  1. 线程安全需求:多线程环境下确保数据不被修改
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i) {
    threads.emplace_back([i]() {  // 值捕获,每个线程有自己的i
        process(i);
    });
}

选择引用捕获的情况

  1. 大型对象:避免复制开销
std::array<int, 1024> big_array;
auto process = [&big_array](int index) {
    big_array[index] *= 2;
};
  1. 需要修改外部变量:累加器、状态更新
int sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int x) {
    sum += x;
});
  1. this指针:成员函数Lambda
class MyClass {
    void method() {
        auto lambda = [this]() { this->member = 42; };
    }
};

选择初始化捕获的情况(C++14)

  1. 需要移动的类型unique_ptrstring
auto task = [buf = std::move(buffer)]() {
    process_buffer(buf);
};
  1. 计算后的值:避免重复计算
auto calc = [prescale = calc_prescale(freq)]() {
    *REG = prescale;
};

嵌入式场景实战

场景1:中断回调安全捕获

class ButtonDriver {
public:
    void init() {
        // 注册中断处理,值捕获确保安全
        register_irq(IRQ_GPIO, [this]() {
            // 需要volatile访问硬件
            if (*GPIO_STATUS & (1 << pin)) {
                *GPIO_STATUS = (1 << pin);  // 清除中断标志
                debounce_count++;
                pending_press = true;
            }
        });
    }

private:
    int pin = 5;
    volatile int debounce_count = 0;
    volatile bool pending_press = false;
};

场景2:DMA传输配置

void start_dma_transfer(const uint8_t* src, uint8_t* dst, size_t size) {
    // 捕获所有参数,确保传输过程中参数稳定
    auto config = [src, dst, size]() {
        *DMA_SRC = reinterpret_cast<uint32_t>(src);
        *DMA_DST = reinterpret_cast<uint32_t>(dst);
        *DMA_CNT = size;
        *DMA_CTRL = DMA_EN | DMA_INT_EN;
    };

    config();  // 应用配置
}

场景3:状态机Lambda

class StateMachine {
public:
    void update() {
        // 初始化捕获,Lambda有自己的状态变量
        auto handle_state = [state = current_state, count = 0]() mutable {
            switch (state) {
                case IDLE:
                    count = 0;
                    state = RUNNING;
                    break;
                case RUNNING:
                    count++;
                    if (count > 100) state = DONE;
                    break;
                case DONE:
                    // ...
                    break;
            }
            return state;
        };

        current_state = handle_state();
    }

private:
    enum State { IDLE, RUNNING, DONE };
    State current_state = IDLE;
};

避免的陷阱

陷阱1:捕获this的潜在问题

class Device {
    std::string name = "sensor";

    // ❌ 如果this指向的对象被销毁,Lambda中的this悬垂
    auto get_name_lambda() {
        return [this]() { return name; };
    }
};

// 正确做法:捕获需要的成员,而不是整个this
auto get_name_lambda_safe() {
    return [name = this->name]() { return name; };
}

陷阱2:循环中的引用捕获

std::vector<std::function<void()>> handlers;

// ❌ 所有Lambda引用同一个i,且循环结束后i失效
for (int i = 0; i < 5; ++i) {
    handlers.push_back([&i]() { use(i); });
}

// ✅ 值捕获,每个Lambda有自己的i
for (int i = 0; i < 5; ++i) {
    handlers.push_back([i]() { use(i); });
}

陷阱3:隐式捕获的隐患

int config = 100;
int temp = 50;

// ❌ [&]捕获了所有变量,包括不需要的temp
auto lambda1 = [&]() { return config > 50; };

// ✅ 明确指定需要捕获的变量
auto lambda2 = [&config]() { return config > 50; };

小结

Lambda捕获机制的关键要点:

  1. 值捕获:安全但复制,适合小型不变数据
  2. 引用捕获:零拷贝但有生命周期要求,适合大型对象
  3. 初始化捕获:C++14最灵活,支持移动和表达式计算
  4. 性能影响:内联后接近零开销,对象大小取决于捕获内容

在嵌入式开发中:

  • 优先使用值捕获或初始化捕获
  • 避免全引用捕获[&]
  • 注意捕获变量的生命周期
  • 善用C++14初始化捕获处理移动语义

Lambda与捕获机制的合理使用,能让你的代码既优雅又高效。