You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何不能像‘给所有对象加const’那样,随意给所有函数加上noexcept?

为何不能像‘给所有对象加const’那样,随意给所有函数加上noexcept?

其实这个问题得从几个角度掰扯清楚——先看看大家常说的「给一切加const」「给一切加constexpr」是啥情况,再对比noexcept的特殊性,最后聊聊为啥const的「不可逆」好像没noexcept那么棘手。

首先说const和constexpr的现状:

  • 我没找到专门讲「const all the things」的视频,但Cpp核心指南里明确有Con.3:默认给指针/引用参数加const;
  • 「constexpr all the things」倒是有不少分享,但constexpr和前两者不一样——它不会改变函数的接口,只是给函数加了「能在编译期执行」的额外功能,就算后来你把constexpr去掉,调用方的代码也不会直接炸掉。

但到了noexcept这里,大家的态度就保守太多了,核心原因就是:noexcept是函数接口的核心组成部分!

  • 尤其是写库的时候,客户端代码会直接依赖函数的noexcept属性——比如有些代码会用std::move_if_noexcept,或者基于「这个函数不会抛异常」做性能优化、逻辑分支。后来要是你想把noexcept去掉,很可能会直接break现有客户端的代码,而且这种破坏可能是隐性的、难排查的。
  • Cpp核心指南里的E.12也明确说:只有当函数绝对不可能抛出异常,或者抛出异常是完全不可接受的情况,才应该用noexcept——这和大家脑补的「给一切加noexcept」完全不是一个尺度。Scott Meyers也强调过:给函数加了noexcept就相当于给了一个不可逆的承诺,几乎没法回头。

这时候有人会问:那const不也是接口的一部分吗?比如把参数声明成const&,后来想改成非const,不也会break客户端?

但实际用下来,「到处加const」好像更经得起时间考验,就算需要调整,也有兼容的空间:
比如举个例子:C++11早期,有人写了这么个函数,用来遍历容器做只读操作:

void work(auto const& r) { 
    for (auto const& e : r) { 
        // 只读逻辑
    } 
}

当时觉得加const&很合理——毕竟这个函数本来就只需要读,为啥要给它写权限?但到了C++20,要是调用方传的是filter_view这种视图,就会编译失败:

std::vector<int> v;
work(v); // 没问题
work(v | std::views::transform(std::identity{})); // 没问题
work(v | std::views::filter(std::identity{})); // 编译失败!

因为filter_view在遍历的时候需要自身做一些修改,而const&不允许它这么做。但解决办法很简单:把函数参数改成auto&&就行,而且原来传容器、传transform_view的代码还能正常跑——也就是说,就算当初加了const,后来调整也不会完全炸掉所有客户端。

但noexcept就没这么灵活了:要是你当初拍脑袋给函数加了noexcept,后来发现实现不得不抛出异常(比如依赖的底层库变了,或者需求加了可能抛异常的逻辑),想去掉noexcept的话,所有依赖这个属性的客户端代码都会出问题,而且这种问题可能是运行时的、难定位的,不像const的问题是编译期就能直接发现的。

所以总结下来:

  • const的接口承诺,就算需要调整,往往有兼容的方式,对现有代码的破坏性相对小;
  • noexcept的承诺是完全不可逆的,它绑定的是函数的异常行为,客户端会基于这个承诺写代码,一旦反悔,代价极高;
  • 这也是为啥大家对「给一切加const」相对宽容,但对「给一切加noexcept」极其谨慎的原因。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 07:08:05