尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C++中noexcept关键字提出动机和使用

C++中noexcept关键字提出动机和使用
📅 发布时间:2026/6/20 17:29:09

一、noexcept是为了解决什么问题?

在 C++11 之前,异常说明使用的是动态异常规范:

voidf()throw(int,std::bad_alloc);voidg()throw();// 表示不抛异常

问题极其严重:

  1. 运行期检查,零优化空间
  2. 违反即调用unexpected(),再terminate()
  3. ABI 不稳定,编译器难以优化
  4. STL 无法据此做容器级别决策

几乎没人敢用


C++11 的设计目标

noexcept的核心动机是:

让“是否会抛异常”成为一个可在编译期推导、可用于优化、可影响接口选择的属性

换句话说:

异常是否发生,从「运行期契约」升级为「类型系统的一部分」


二、noexcept的本质语义(非常重要)

voidf()noexcept;

并不是说:

“这个函数不会抛异常”

而是说:

如果这个函数抛异常,程序将立刻调用std::terminate()

即:

try{f();}catch(...){std::terminate();// 无条件}

noexcept是承诺,不是能力检测。


三、noexcept的两种形式

1.无条件noexcept

voidf()noexcept;

等价于:

voidf()noexcept(true);

2.条件noexcept(C++11 核心设计)

template<typenameT>voidfoo(T&&x)noexcept(noexcept(T(std::forward<T>(x))));

异常规格成为编译期表达式

示例:完美转发构造

template<typenameT>Tmake()noexcept(noexcept(T())){returnT();}

四、noexcept是类型系统的一部分

voidf()noexcept;voidg();usingF=void(*)();usingNF=void(*)()noexcept;F pf=g;// OKNF pnf=f;// OK

但:

NF pnf=g;// 编译错误

noexcept是函数类型签名的一部分。


五、为什么noexcept对性能至关重要?

1.影响代码生成(EH tables)

  • 有异常 → 生成异常展开表
  • noexcept→完全移除异常元数据

在 hot loop / 数值计算 / SLAM 后端中尤为关键。


2.STL 的核心决策依据

std::vector扩容行为

if(T isnoexcept-move-constructible)使用 moveelse使用 copy

等价于:

std::is_nothrow_move_constructible_v<T>

示例:为什么没写noexcept会导致性能灾难

structBad{Bad(Bad&&){}// 没有 noexcept};structGood{Good(Good&&)noexcept{}};
std::vector<Bad>v1;// 扩容时 copystd::vector<Good>v2;// 扩容时 move

这就是 STL 要求 move ctornoexcept的原因


六、noexcept与移动语义的关系(核心)

Rule of Five + noexcept

structX{X(X&&)noexcept=default;X&operator=(X&&)noexcept=default;};

原因

  • vector,deque,map等容器
  • std::optional,std::variant
  • std::unique_ptr

全部依赖noexcept来选择移动路径


七、noexcept与析构函数(极其重要)

C++11 起

~T()noexcept(true);// 默认

即:

析构函数隐式noexcept

如果析构函数抛异常?

~T(){throwstd::runtime_error("boom");}

std::terminate()

原因:防止 stack unwinding 二次异常


正确模式

~T()noexcept{try{cleanup();}catch(...){log_error();}}

八、noexcept与模板元编程

常见 trait

std::is_nothrow_move_constructible<T>std::is_nothrow_copy_constructible<T>std::is_nothrow_destructible<T>

典型应用(SLAM / 点云库中很常见)

template<typenameT>voidsafe_swap(T&a,T&b)noexcept(std::is_nothrow_move_constructible_v<T>&&std::is_nothrow_move_assignable_v<T>){T tmp=std::move(a);a=std::move(b);b=std::move(tmp);}

九、noexceptvsconst

属性是否属于类型
const是
noexcept是
throw()否(已废弃)

十、常见误区(非常重要点)

误区 1:noexcept= 不会抛异常

事实:
noexcept的语义是“一旦抛异常,立即std::terminate()”


错误理解示例

#include<iostream>#include<stdexcept>voidf()noexcept{std::cout<<"before throw\n";throwstd::runtime_error("boom");std::cout<<"after throw\n";}intmain(){f();}

运行结果

before throw terminate called after throwing an instance of 'std::runtime_error'
  • catch根本来不及
  • 栈不会正常展开
  • 析构函数不会全部执行

对比:非noexcept

voidg(){throwstd::runtime_error("boom");}intmain(){try{g();}catch(conststd::exception&e){std::cout<<"caught: "<<e.what()<<'\n';}}

输出

caught: boom

正常异常语义


工程结论

noexcept是“强终止契约”,不是“不会抛”的保证


误区 2:随便给函数加noexcept

这是生产事故级错误


错误示例:包装函数

voidmay_throw(){throwstd::runtime_error("error");}voidwrapper()noexcept{may_throw();//}intmain(){wrapper();}

运行结果

terminate called after throwing an instance of 'std::runtime_error'

更隐蔽的版本(真实工程坑)

voidlog(conststd::string&s){if(s.empty()){throwstd::logic_error("empty");}}voidfoo()noexcept{log("");// 间接抛异常}

根本看不到 throw,却直接 terminate


正确写法 1:内部吞异常

voidfoo()noexcept{try{log("");}catch(...){// fallback / logging}}

正确写法 2:条件noexcept

template<typenameF>voidcall(F&&f)noexcept(noexcept(f())){f();}

工程结论

只有当“整个调用链都不抛异常”时,才可以写noexcept


误区 3:忘记给 move ctor 加noexcept

这是 STL 性能退化最常见的来源


错误示例

#include<vector>structBad{Bad()=default;Bad(constBad&)=default;Bad(Bad&&){}// 没有 noexcept};intmain(){std::vector<Bad>v;v.reserve(1);v.emplace_back();v.emplace_back();// 触发扩容}

STL 的真实逻辑

if(is_nothrow_move_constructible<T>)moveelsecopy

结果

  • 扩容时调用 copy ctor
  • 大对象 →灾难性性能
  • 对 Eigen / 点云 / 位姿对象尤其致命

正确示例

structGood{Good()=default;Good(constGood&)=default;Good(Good&&)noexcept{}//};

对比验证(可加日志)

structVerbose{Verbose()=default;Verbose(constVerbose&){std::cout<<"copy\n";}Verbose(Verbose&&)noexcept{std::cout<<"move\n";}};
std::vector<Verbose>v;v.emplace_back();v.emplace_back();

输出

move

如果去掉noexcept,输出是:

copy

工程级总结

一个没写noexcept的 move ctor,等价于“禁用移动语义”


三个误区一句话总结

误区本质错误
noexcept= 不会抛实际是“抛了就死”
随便加noexcept违反调用链异常安全
move ctor 没noexceptSTL 主动退化到 copy

十一、工程级使用准则

必须noexcept

场景
移动构造 / 移动赋值
析构函数
swap
RAII cleanup
数值内核、实时系统

谨慎使用

场景
构造函数(分配内存)
IO
用户回调

不要使用

场景
无法保证内部调用链不抛异常

十二、一个完整工程示例

structPose{Eigen::Matrix4d T;Pose()=default;Pose(Pose&&other)noexcept:T(std::move(other.T)){}Pose&operator=(Pose&&other)noexcept{T=std::move(other.T);return*this;}~Pose()noexcept=default;};

这类类型在SLAM 后端、图优化、点云容器中是黄金标准


十三、总结一句话

noexcept不是语法糖,而是现代 C++ 性能、异常安全和库设计的核心支点

相关新闻

  • DAY18 机器学习
  • a星学习记录 通过父节点从目的地格子坐标回溯起点
  • jd.item_review获取京东商品评论 及tb.item_review获取taobao商品评论

最新新闻

  • 2026上海留学中介深度测评 - 资讯速览
  • LLM-Engineering-Essentials高级课程:大模型微调与DPO技术实践
  • MCX W23超低功耗蓝牙SoC:如何实现微型IoT设备的续航与安全突破
  • JMeter接口测试实战:从环境搭建到多接口串联与结果分析
  • 【大模型应用开发-实战】(四)nvitop: 史上最强GPU性能实时监测工具
  • Selenium 4升级指南:解决executable_path报错与驱动管理最佳实践

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号