网易首页 > 网易号 > 正文 申请入驻

看懂 lambda,你就懂 C++11 在补哪一块历史窟窿

0
分享至

lambda 不是突然冒出来的。

它是 C++ 很长一段历史里。

“回调”和“算法”把人逼出来的产物。

九十年代 STL 把算法搬进标准库。

sortfind_iffor_each 这些东西一出来。

大家就开始不断地问一个问题。

比较规则放哪。

过滤条件放哪。

那会儿最省事的做法是函数指针。

bool by_length(const std::string& a, const std::string& b) {
return a.size
}

能用。

但它带不走上下文。

你想要一个“阈值”。

想要一个“表”。

想要一个“配置”。

函数指针就开始装死。

于是 C++ 的老派解法登场。

函数对象。

也就是写个类。

把状态塞进去。

再实现 operator

它很强。

也很啰嗦。

而且只要你写过一堆一次性比较器。

你就会明白。

这不是“工程设计”。

这是“为了写一行算法调用,先铺十行仪式”。

再后来。

社区开始用库来补洞。

Boost 里就出现过好几代“库级 lambda”。

表达力很猛。

代价也很猛。

编译错误像瀑布。

编译时间像冬天。

这些尝试的价值不在于让你天天用。

它们更像是在跟委员会说。

需求已经摆在桌上了。 不要逼大家继续用库硬凑。

到了 C++0x(后来改名 C++11)。

标准委员会要解决的不只是“写起来帅不帅”。

而是两件更朴素的事。

让算法和回调能在现场写。

让状态能安全地跟着走。

于是 lambda 作为语言特性进了标准。

它的语法也很直白。

是口袋。

口袋里装捕获。

是参数。

{} 是函数体。

下面我们先从“没有 lambda 的年代”开始。

你会看到。

它到底替你省掉了哪些仪式。


没有 lambda 的年代

先看一个最常见的场景。

按长度排序字符串。

C++11 之前你经常这么写。

#include
#include
#include

struct ByLength {
booloperator(const std::string& a, const std::string& b) const{
return a.size
}
};

int main {
std::vectorstd::string> v{"aaa", "b", "cc"};
std::sort(v.begin, v.end, ByLength{});
}

能用。

也够“正统”。

但它有一个很现实的问题。

这段比较逻辑只在这里用一次。

你还是得给它起名字。

还得给它放个地方。

最后你得到一堆“只用一次的类型”。

代码像抽屉里的塑料袋。

越攒越多。


lambda 的核心:把临时函数写在原地

C++11 的 lambda 长这样。

 {
}

它看起来像三块。

{}

我习惯把它理解成。

“我现在要在这里造一个函数对象”。

最简单的例子。

auto f =  {
return42;
};


int x = f;

f 不是函数指针。

它是一个匿名类型的对象。

它有一个 operator

所以你可以像调用函数一样调用它。

这就是为什么很多人说。

lambda 的本质是“匿名函数对象”。


回到排序:把比较逻辑贴回现场

刚才那段排序。

现在可以写成这样。

#include
#include
#include

intmain {
std::vectorstd::string> v{"aaa","b","cc"};

std::sort(v.begin, v.end,
(const std::string& a, const std::string& b) {
return a.size
});
}

你一眼就知道它在比什么。

你也不用去找 ByLength 在哪。

这对“读代码”来说,是实打实的效率。


那对方说的“闭包”到底是什么

你会听到另一句话。

lambda 是闭包。

别被术语吓到。

它讲的其实就是一件事。

lambda 可以“带着环境一起走”。

也就是捕获。


捕获:把外面的变量带进来

先看一个最朴素的。

我们想筛掉长度小于某个阈值的字符串。

阈值是运行时决定的。

#include
#include
#include

intmain {
std::vectorstd::string> v{"aaa","b","cc"};
std::size_t limit =2;

auto it = std::find_if(v.begin, v.end,
[limit](conststd::string& s) {
return s.size >= limit;
});

(void)it;
}

这里的 [limit] 就是捕获列表。

它表示。

把外面的 limit 按值拷贝一份。

拷贝进这个 lambda 对象里。

所以它才叫“闭包”。

它把 limit 这份上下文封在里面了。


按值捕获 vs 按引用捕获

按值捕获。

安全。

但它不会跟着外界变化。

int x = 1;
auto f = [x] { return x; };


x =2;
int v = f;// 还是 1

按引用捕获。

灵活。

但你就要开始关心生命周期。

int x = 1;
auto f = [&x] { return x; };


x =2;
int v = f;// 变成 2

这就是工程里经常踩的坑。

lambda 把引用带走了。

变量却早就死了。

尤其是你把 lambda 塞进回调。

塞进线程。

塞进异步任务。

那就更容易“看着没问题”。

然后在某个晚上炸。


让 lambda 修改按值捕获:mutable

C++11 里。

按值捕获默认是只读的。

int x = 1;


auto f = [x] {
// x++; // ❌ 不允许
return x;
};

如果你确实想改那份“拷贝进来的 x”。

可以加 mutable

int x = 1;

auto f = [x] mutable {
x++;
return x;
};

int a = f;// 2
int b = f;// 3

注意。

你改的是闭包对象内部的那份副本。

不是外面的 x


捕获 this:很方便,也很危险

成员函数里写 lambda。

很自然会捕获 this

#include

struct Worker {
int base =10;

std::functionint(int)>make {
return [this](int x) {
return base + x;
};
}
};

这段代码能跑。

但风险也很明显。

你把 lambda 返回出去了。

lambda 里握着 this

如果 Worker 先销毁。

lambda 再被调用。

那就是悬空指针。

很多“回调偶现崩溃”。

背后就是这种故事。

在 C++11 里。

你得自己保证对象活得比回调久。

或者让回调捕获 shared_ptr

别让 this 裸奔。


返回类型:大多数时候不用写

lambda 的返回类型通常可以自动推导。

auto f = (int x) {
return x +1;
};

但有一个经典场景会卡住。

分支返回不同类型。

auto g = (bool ok) {
if (ok) {
return1;
}
return0;// 这还好
};

如果分支里返回的类型不一致。

你就需要明确写返回类型。

auto h = (bool ok) -> int {
if (ok) {
return1;
}
return0;
};

C++11 的规则比较朴素。

它希望你别让编译器猜你到底想要什么。


把 lambda 放到哪里

lambda 的类型是匿名的。

所以你最常见的接法是 auto

auto pred = (int x) { return x > 0; };

但有时你需要把它存到容器里。

或者做成接口返回。

这时很多人会用 std::function

#include


std::functionint(int)> f = (int x) { return x +1; };

这能用。

代价也真实。

std::function 是类型擦除。

可能有一次堆分配。

也会引入间接调用。

性能敏感路径上。

你要心里有数。

如果你在写模板库。

更常见的做法是。

让调用方把 lambda 作为模板参数传进来。

template class F>
int apply(F f, int x) {
return f(x);
}


intmain {
int v = apply((int x) { return x +1; },41);
(void)v;
}

这样通常可以内联。

也不会产生类型擦除的开销。


lambda 和线程:最容易踩的一个雷

你把 lambda 丢给线程。

最常见的坑是。

按引用捕获了一个局部变量。

然后线程还没跑完。

局部变量就没了。

#include


std::threadspawn {
int x =42;
return std::thread([&] {
(void)x;
});
}

这段代码的危险点不在语法。

在生命周期。

最稳的办法。

要么按值捕获。

要么把需要的东西搬进线程对象里。

你别让线程去借一个“马上要还的变量”。


小结

lambda 不是为了炫技。

它解决的,是“临时小逻辑到处放”的老毛病。

它让使用点和逻辑点贴在一起。

读代码的人少搬家。

写代码的人少起名。

但它也把一个更老的工程问题推到你面前。

生命周期。

你捕获什么。

捕获的是值还是引用。

this 能不能安全带出去。

这些问题。

在没有 lambda 的年代也存在。

只是那时你写得更啰嗦。

所以更容易意识到“我在把东西带走”。

到了 lambda。

一切变顺手了。

也就更容易顺手埋雷。

写得爽。

也得写得清醒。


结尾

我是 everystep 的作者。

写这些文章只有一个目的: 帮你在真实项目里,把 C++ 设计模式用顺、用稳,少踩点坑。

如果你看到这里,说明你大概率也遇到过这些情况:

  • 书上的设计模式看得懂,一写成 C++ 工程代码就又回到 if-else 垃圾场;

  • 老项目 if-else 成片,改一个需求就牵一发动全身,改完心里没底;

  • 想系统补齐 C++ 基础和设计模式,又不想在零散收藏里来回翻。

为了让你少走点弯路,现在你可以立刻拿走这三样东西:

  1. 一条往后看的路线

    关注 everystep ,后面会按「模式 → 场景 → 重构前后代码对比」的节奏,继续拆 C++ 设计模式和工程实践。

  2. 一份随时翻的基础手册

    在公众号后台发送关键词 「C++基础」 ,我会把目前整理好的基础文章打包成 PDF 发给你,方便离线阅读和按关键词快速查找。

  3. 两篇可以马上上手练的长文

  • 想用现代 C++ 写点“真家伙”?来手搓一个 mini-Redis。

  • 想知道 Git 背后到底在干嘛?这篇“从零实现 Git”讲透了。

你的每一次关注、在看和转发,都是我继续把这些坑填下去的理由。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
这天,一排黑皮肤、褶子脸的大叔,秒了内娱一堆涂脂抹粉的小鲜肉

这天,一排黑皮肤、褶子脸的大叔,秒了内娱一堆涂脂抹粉的小鲜肉

真的八卦小学弟
2025-12-26 16:00:06
彻底凉凉!5000辆合规网约车,一单没接!

彻底凉凉!5000辆合规网约车,一单没接!

网约车焦点
2025-12-26 10:40:35
谢春涛率中共代表团赴柬埔寨、老挝宣介中共二十届四中全会精神

谢春涛率中共代表团赴柬埔寨、老挝宣介中共二十届四中全会精神

新华社
2025-12-26 17:00:06
楼上邻居把卫生间改成衣帽间,我就看看不说话,6个月后哭着拆掉了

楼上邻居把卫生间改成衣帽间,我就看看不说话,6个月后哭着拆掉了

绘本家居
2025-12-25 11:50:18
黑瞎子岛零下30℃直播! 72岁董明珠把格力焊死在品质上

黑瞎子岛零下30℃直播! 72岁董明珠把格力焊死在品质上

别人都叫我阿腈
2025-12-26 10:44:08
歼-36:当我消失在雷达里,世界才开始害怕,5.2马赫的沉默

歼-36:当我消失在雷达里,世界才开始害怕,5.2马赫的沉默

正直小墨
2025-12-26 21:51:07
曝李晨范冰冰曾领证,女方落难男方筹几千万,男方家看不上范冰冰

曝李晨范冰冰曾领证,女方落难男方筹几千万,男方家看不上范冰冰

古希腊掌管月桂的神
2025-12-26 18:38:26
那个男人回来了!马丁内斯领衔曼联防守大战,阿莫林变阵获奇效

那个男人回来了!马丁内斯领衔曼联防守大战,阿莫林变阵获奇效

夜白侃球
2025-12-27 10:49:10
不到24小时美国扣押第三艘油轮,遭遇强硬对手引发海上追逐战

不到24小时美国扣押第三艘油轮,遭遇强硬对手引发海上追逐战

优趣纪史记
2025-12-23 16:23:38
喜提奖金+汽车!张本智和对着100名日本人发誓:世乒赛还要拿冠军

喜提奖金+汽车!张本智和对着100名日本人发誓:世乒赛还要拿冠军

风过乡
2025-12-26 21:38:03
要下雪了!大范围影响江苏!

要下雪了!大范围影响江苏!

鲁中晨报
2025-12-27 09:01:03
海南封关成照妖镜,东南亚国家挨个现行,新加坡直言不准自给自足

海南封关成照妖镜,东南亚国家挨个现行,新加坡直言不准自给自足

眼底星碎
2025-12-26 19:35:35
突然伤停4周!湖人重启交易,东契奇无语了

突然伤停4周!湖人重启交易,东契奇无语了

德译洋洋
2025-12-27 12:53:44
中国最重要的“东西大动脉”,要来了

中国最重要的“东西大动脉”,要来了

国民经略
2025-12-26 11:47:35
A股:历史或将重演,不出意外的话,十二月底,股市很可能这样走

A股:历史或将重演,不出意外的话,十二月底,股市很可能这样走

深析古今
2025-12-27 11:08:31
我给局长当十年秘书,告别拥抱他躲开,隔天市委叫我见新书记

我给局长当十年秘书,告别拥抱他躲开,隔天市委叫我见新书记

晓艾故事汇
2025-12-15 08:13:39
刘宇宁回应“未进组是只想接一番”:我的价值不需要靠番位来证明

刘宇宁回应“未进组是只想接一番”:我的价值不需要靠番位来证明

韩小娱
2025-12-27 09:46:32
洪森最大的失误:低估了西哈莫尼国王,高估了儿子洪玛奈!

洪森最大的失误:低估了西哈莫尼国王,高估了儿子洪玛奈!

阿柒的讯
2025-12-23 18:22:55
你听过最劲爆的瓜是啥?网友:被大八岁的补习班老师表白了

你听过最劲爆的瓜是啥?网友:被大八岁的补习班老师表白了

带你感受人间冷暖
2025-11-26 00:10:06
晋中一村民称家中120只羊被毒死,其中110只是怀孕母羊,投毒者是常和父亲一起喝酒的好友,案发后还假装来安慰

晋中一村民称家中120只羊被毒死,其中110只是怀孕母羊,投毒者是常和父亲一起喝酒的好友,案发后还假装来安慰

极目新闻
2025-12-26 17:19:15
2025-12-27 13:31:00
娱乐督察中
娱乐督察中
独乐乐不如众乐乐
179文章数 20336关注度
往期回顾 全部

科技要闻

小米也涨价了!业界称终端再不涨明年必亏

头条要闻

贾国龙首次回应西贝风波 称自己连续40天靠安眠药入睡

头条要闻

贾国龙首次回应西贝风波 称自己连续40天靠安眠药入睡

体育要闻

NBA教练圈的布朗尼,花了22年证明自己

娱乐要闻

刘宇宁:我的价值不需要靠番位来证明

财经要闻

注意,开始拉物价了!

汽车要闻

好音响比大屏更重要?车企开始“听”用户的

态度原创

教育
家居
艺术
健康
公开课

教育要闻

3甲=2乙,就他们的乘积

家居要闻

格调时尚 智慧品质居所

艺术要闻

毛主席致徐悲鸿信件曝光,书法风格引关注。

这些新疗法,让化疗不再那么痛苦

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版