输入关键词开始搜索

C++ Lambda 与函数对象

基础语法

// [捕获](参数) -> 返回类型 { 函数体 }
auto add = [](int a, int b) -> int { return a + b; };
auto add2 = [](int a, int b) { return a + b; };  // 返回类型可省略

cout << add(3, 5);  // 8

捕获列表

int x = 10, y = 20;

// 按值捕获(默认不可修改)
auto f1 = [x, y] { return x + y; };  // 捕获副本

// 按引用捕获
auto f2 = [&x, &y] { x++; y++; };    // 修改外部变量

// mutable — 允许修改值捕获的副本
auto f3 = [x]() mutable { x++; return x; };

// 默认捕获方式
auto f4 = [=] { return x + y; };     // 全部按值
auto f5 = [&] { x++; y++; };         // 全部按引用

// 混合捕获
auto f6 = [=, &y] { return x + y; }; // y 引用,其余值
auto f7 = [&, x] { return x + y; };  // x 值,其余引用

// C++14 初始化捕获(移动)
auto p = make_unique<int>(42);
auto f8 = [p = move(p)] { return *p; };

// C++14 捕获表达式
auto f9 = [y = x + 5] { return y; };  // 捕获时计算一次

捕获的本质

int x = 42;
auto lambda = [x] { return x; };

// 编译器大致生成:
class __lambda {
    int x;  // 按值捕获 = 成员变量
public:
    __lambda(int arg) : x(arg) {}
    auto operator()() const { return x; }  // 调用运算符
};
// 按引用捕获 = 成员变量是引用类型

实战场景

STL 算法

vector<int> v = {1, 2, 3, 4, 5, 6};

// 条件删除
v.erase(remove_if(v.begin(), v.end(),
    [](int x) { return x % 2 == 0; }), v.end());

// 自定义排序
sort(v.begin(), v.end(),
    [](int a, int b) { return abs(a) < abs(b); });

// 查找
int threshold = 3;
auto it = find_if(v.begin(), v.end(),
    [threshold](int x) { return x > threshold; });

// 变换
transform(v.begin(), v.end(), v.begin(),
    [](int x) { return x * x; });

回调与事件

// 按钮点击
button->onClick([this]() {
    m_label->setText("clicked");
});

// 定时器
QTimer::singleShot(1000, [this]() {
    m_status = Status::Timeout;
});

// 线程
thread t([](int n) {
    cout << "Thread: " << n;
}, 42);
t.join();

即调 Lambda(IILE)

// 复杂的 const 变量初始化
const auto config = [&]() {
    map<string, int> m;
    m["timeout"] = 5000;
    m["retries"] = 3;
    return m;
}();  // 注意尾部的 () — 立即调用

std::function

#include <functional>

// 类型擦除 — 可容纳任何可调用对象
function<int(int, int)> fn;

fn = [](int a, int b) { return a + b; };     // lambda
fn = plus<int>();                              // 函数对象
fn = [](int a, int b) { return a * b; };      // 换一个 lambda

// 存储回调
class Button {
    function<void()> m_callback;
public:
    void onClick(function<void()> cb) { m_callback = move(cb); }
    void click() { if (m_callback) m_callback(); }
};

性能考量

方式开销适用场景
裸 Lambda零(等同于函数对象)模板参数传递
std::function堆分配 + 虚调用类型擦除、存储回调
函数指针不可捕获C API 兼容
// ❌ std::function 有不必要的开销
template <typename F>
void forEach(vector<int> &v, F fn) {  // 编译期确定类型,零开销
    for (auto &x : v) fn(x);
}

// 只有在需要运行时多态时才用 std::function
vector<function<void()>> callbacks;
callbacks.push_back([=] { doA(); });
callbacks.push_back([=] { doB(); });

常见陷阱

// 陷阱 1: 按引用捕获后,Lambda 比变量活得久
function<void()> createCallback() {
    int local = 42;
    return [&local] { cout << local; };  // ❌ 悬空引用!
}
// 修正:按值捕获 → [local]

// 陷阱 2: this 捕获 + 异步 = 对象可能已析构
// 修正:用 shared_from_this() 或 weak_ptr

// 陷阱 3: mutable Lambda 中按值捕获不会影响外部
int n = 0;
auto f = [n]() mutable { n++; };
f();  // n(内部副本) = 1
// n(外部) 还是 0