@
Nugine0 这才是最扼腕的地方:这些场景明明客观上就是显然的大多数,但是很多用户就会因为自己使用的语言和习惯为缺陷辩护,而不是直面不足。
在工程上,很多时候经验上的“大多数”足够决定一个决策的正确性。反其道而行之是不明智的。这跟银弹完全是两回事。以银弹为理由而拒绝重新审视现有实践,逃避全面分析问题的成本和收益,是不成熟的优化的一种。
Google C++ 规范不用异常,简而言之就是菜。
Google 相关人士有提到过,他们知道现代 C++ 默认就该放手用户使用异常,但是他们自己现有代码的历史包袱太重,没法承担放开手下人用异常的质量风险。
技术上提出的问题全是比较好笑的:
* 他们认为加异常就需要遍历所有实现的路径。
这直接就不是默认情况——并且说明原来的代码质量普遍就很糟糕,没写清楚接口约束,也习惯用户不看这类“错误条件”就瞎用。
* 他们认为用了异常就难以看清逻辑。
大体同上,只是更偏向于内部接口的实用。(另外类似地,是不是能认为用了赋值就难看清逻辑?其实某些 PFP 教徒还真是那么想的……虽然他们甚至未必都拎清楚这是为了 equational reasoning 这回事。)
* 他们认为写出异常安全的代码是负担。
但事实上异常安全不是线程安全那样需要另外加逻辑才能维持可组合性的属性,不引入异常安全操作,整个程序默认都是异常安全的,就像 Rust 你不去用 unsafe 默认就不用管一些问题——区别无非是 Rust 有明确的关键字和编译器强制检查,这里依赖自觉;但这个自觉工程上很容易做到,甚至人肉检查都没多大开销(特别是按标识符索 poisoned 属于最容易 review 中找茬刷 KPI 的一类,比找拼写错误都容易)。
只有滥用破坏安全性不变量的低级接口才会有这问题。类似地,正常人代码禁绝 malloc/new (不担保资源所有权不变量,恰好同样不担保异常安全)满天飞就能几乎完全静态避免内存泄漏(除了循环引用)。这逻辑就像“写出不泄漏的代码代价太大,学智能指针( RAII )麻烦,所以还不如老老实实用 malloc/new”一样可笑。更荒谬的地方在于,这些代码组合进启用了异常且正确实现了异常安全的正常 C++代码以后,还可能因为这类局部的不安全的传播污染整个程序而使整个程序的安全性失效,基本就快直说“Google 的 C++代码别指望给正常 C++代码复用”了。( Google 还敢在自家开源项目里鼓吹这个,呵、呵。)
实际 Google 倒是有老实用智能指针而不一定同时具有所有的具体问题,但同样缺乏普遍保证,而且不少是自家比 std 劣等残次多了的发明。
* 异常很可能存在额外开销。这是唯一一个真实的工程问题,但是 Google 绝大多数项目实际根本踩不到这里的问题。
即便是这个问题也不是 C++ 自己作死的原因,TR 18015 指出语言上没直接开销(当然这里对空间开销分析不足,但你指望一个提了 stack unwinding 却连什么叫 stack 都说不清楚、多大 satck overrun UB 的语言啥呢)。
实际开销绝大多数问题都是对 ABI 的不切实际的假设。这些也不只是异常,unique_ptr 因为就因为写 SysV ABI 的 C 厨的不走心在 *NIX 实现里走不了寄存器,还得编译器开洞没法默认用。
如果这种问题算回避不了的开销,那么 C++ 早就是性能洼地了。实际呢?不用 C++ ,用 C 用汇编或者其它手段能写出跟 C++ 实现相比像样的代码用户有多少?
退一步讲,就算不深究造成这些历史包袱的责任,这些问题的坑也都是合格的 C++ 用户自动会绕开的,反而要改变习惯还费事(比如 new 得多加 nothrow 免得换到正常配置里直接呵呵了)。你旧代码多,所以新项目代码也跟着喂食,这什么姿势?
就这点原因都敢写到全公司的规范,Google 这里的技术水平整体(先不说个别 team )远不如微软之类,能和鹅比都不错了。
我建议分析 Google 这些规范的不靠谱性列入任何想用 C++ 正经干活的组织的标准面试流程。