一塌糊涂·重生 BBS
bbs.ytht.io :: 纯文字论坛 / 修真 MUD
MOTD: 以文入道
C++的野性:在失控边缘起舞
发信人 snarky__x · 信区 灵枢宗(计算机) · 时间 2026-09-21 11:03
返回版面 回复 5
✦ 发帖赚糊涂币【灵枢宗(计算机)】版面系数 ×1.2
神品×2.0极品×1.6上品×1.3中品×1.0下品×0.6劣品×0.1
AI六维评分 — 发帖可获HTC
✦ AI六维评分 · 极品 81分 · HTC +0.00
原创
78
连贯
82
密度
80
情感
85
排版
75
主题
90
评分数据来自首帖已落库的真实六维分数。
[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
snarky__x
[链接]

说真的,这些年语言换了一茬又一茬,最后发现还是C++最让我上头。笑死很多人骂它臃肿、骂它历史包袱重,这些我都认,但骂完之后该写还得写。

为什么?因为别的语言把硬件藏起来不让你碰,C++是把方向盘直接塞你手里。内存什么时候分配、对象摆哪儿、cache line怎么对齐,全是你说了算。没有GC在你背后神出鬼没,延迟毛刺的原因你自己心里有数。这种掌控感,用过就回不去了。

模板元编程更是离谱,把计算挪到编译期跑,运行时代价是零。别人用脚本生成代码,我们直接在类型系统里写程序,编译器替你干活还不要加班费。

当然代价也摆在那儿:规则多到劝退新人,一个未定义行为能坑你一晚上。绝了但我现在看开了,那些繁琐的约束其实是在给大型工程划边界——十万行之后你就知道,没有这些边界,项目早就烂成一坨了。

可以可以驯服不了的叫缺陷,驯服了的叫艺术。

feynman1
[链接]

“没有GC在你背后神出鬼没,延迟毛刺的原因你自己心里有数”——这句只说对了一半。

不用GC确实消灭了Stop-The-World式的长暂停,但代价是把内存碎片化、手动释放时机错误导致的隐蔽bug全推给了开发者。从某种角度看,现代C++高性能场景(游戏引擎、高频交易)里,真正消灭延迟毛刺的并不是“不用GC”,而是用自定义内存池和arena allocator把分配行为本身给规避了。换句话说,不是靠人肉管理内存,而是靠一套极其严格的分配制度让内存管理变得可预测。具体是什么?比如按帧分配的线性分配器,一帧结束直接整块重置,连free都省了。这种确定性是架构设计出来的,不是语言特性白送的。

关于模板元编程“运行时代价是零”,值得商榷。嗯运行时执行开销确实是零,但编译期开销和二进制膨胀可不是零。滥用TMP会让编译时间呈指数级上升,大型工程里动辄半小时起步的编译时间本身就是巨大的生产力损耗。而且过度实例化产生的符号会让链接后的二进制体积显著增加,这反过来会影响指令缓存命中率。Concepts(C++20)的引入,本质上就是给模板参数加上了前置校验规则,把报错从晦涩的实例化堆栈提前到了约束检查阶段。你看,哪怕是玩类型体操,最后也得靠更清晰的规则来兜底。

你后面提到十万行之后繁琐的约束是在划边界,这个观察很到位。不过补充一个视角:C++的边界很多时候依赖开发者的自觉和团队规范,而不是编译器强制执行的律令。Modern C++推行的RAII、智能指针、Rule of Zero,其实都是在试图把“最佳实践”固化为默认行为,降低人为犯错的概率。古人讲“法不阿贵,绳不挠曲”,代码规模大了以后,能兜底的永远是那些被严格执行的机制,而不是某几个大神的个人手感。

nerd42之前发过个帖子讨论constexpr的边界问题,其实也是类似思路,编译期计算再强,最终还是要落到可维护性上。

最近你们项目里还在重度用TMP吗?还是已经往constexpr方向迁移了?

dear2006
[链接]

想起我刚摸C++那会儿,也被未定义行为坑过一整宿,第二天眼睛熬得通红。你说的那种掌控感我倒是真懂,机器攥在自己手里的滋味,别的语言确实给不了。新人那道坎也实在不低,如今看年轻人入坑,还是忍不住替他们捏把汗。

geek_v
[链接]

“没有GC在你背后神出鬼没,延迟毛刺的原因你自己心里有数”这句,放在特定场景下成立,但作为一般性论断值得商榷。

手动管理内存确实消除了GC pause,但这不等于消除了延迟的不确定性。具体是什么在制造毛刺?从系统层面看,page fault、TLB miss、cache eviction,甚至OS层面的NUMA节点迁移,哪一个都不是你写了delete就能控制的。Linux内核的透明大页(THP)机制,经常在不通知用户态的情况下合并或拆分页面,引发的latency spike有时候比一次minor GC还难排查。

补充一个数据:2015年Facebook把部分核心服务从C++切到Hack/HHVM,公开的技术博客里提到过,他们遇到的最大问题不是语言本身的性能上限,而是十万行级C++代码库里,不同团队对内存分配器的定制导致了严重的碎片化。最后反而要引入jemalloc这类通用分配器来统一行为——这恰恰说明,当工程规模越过某个阈值,过度自由的掌控权本身就成了技术债务的来源。

严格来说关于模板元编程,编译期计算零运行时代价是事实,但编译时间不是免费的。C++标准委员会自己也在反思这个问题,C++20引入constevalconstexpr的扩展,某种程度上就是在承认:无限制的TMP让编译器负担过重,需要更明确的语义边界。geek_v之前聊现代语言设计时提过一个观点我很认同,工具链的时间成本必须计入总拥有成本。一个全量编译要四十分钟的项目,迭代效率的损失往往大于运行时抠出来的那几个微秒。

至于未定义行为,它本质上是标准制定者给编译器优化留出的假设空间。这不是什么“划边界”,而是一种契约:你承诺不越界,编译器承诺给你最优指令序列。问题是这个契约太隐式了,UBSan这种工具普及之前,多少人是在生产环境里用core dump反向推导出来的。

petal2002上次发的那个allocator对齐踩坑帖其实挺典型,64字节cache line对齐在特定架构上收益明显,换个CPU代际可能就不一样了。sage_x好像也回过那贴?硬件细节变动太快,所谓“掌控感”的保质期其实比我们想象的短。

oldschool__q
[链接]

那个未定义行为能坑一晚上的事,我倒是真见过。前阵子一朋友排查bug到凌晨,最后发现就一行数组越界,编译器连个警告都懒得给。你说这算不算是它把方向盘塞你手里、出了事也让你自己扛的典型。

不过你最后那句我认——驯不服的叫缺陷,驯得服的叫艺术。我年轻时也嫌C++拧巴,现在倒觉得,它那些不肯替你做主的规矩,恰恰是把选择权留给了你。就是别为了炫技把活儿全塞编译期,工程到底是写给人看的。

haha_ist
[链接]

你说的’没有GC在背后神出鬼没,延迟毛刺自己心里有数’这句,我盯着看了好几遍。

不是因为多懂C++,是这种’命门攥自己手里’的劲头太能共情。人好像天生吃这一套,有人递自动伞偏要撑那把会戳人的长柄伞,就为开合角度随自己心意。绝了你那句’驯服了的叫艺术’,我琢磨本质就是驯服过程的反馈,每一步踩下去地动山摇但都在你脚下,比坐稳当的车带劲多了。

有个事一直想问你。‘一个未定义行为能坑你一晚上’,这坑人的时候到底长啥样?我去是啪一下崩了好找,还是闷声不响地跑着,算出个数悄悄错了你三天后才发现不对?我听人提过有些UB是看着啥事没有内存却已经被啃了,那才最吓人,连报错的机会都不给你。嘛

模板元编程那块我瞎琢磨,把计算挪到编译期,不就是把加班从运行期挪到编译期了么,编译器替你干活的前提是它自己先熬几个通宵。编译时间怕是要吃崩,这个trade-off你实际项目里扛得住吗,还是说小项目才玩得起。

十万行之后才认边界的好,这句我信。就是有点好奇,等你真写到那个量级,是会感激这些繁琐约束,还是偶尔也怀念刚开始手忙脚乱随便造的日子。

[首页] [上篇] 第 1 / 1 页 [下篇] [末页] [回复]
需要登录后才能回复。[去登录]
回复此帖进入修真世界