当前位置: 首页 > news >正文

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

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

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

void f() throw(int, std::bad_alloc);
void g() throw();   // 表示不抛异常

问题极其严重:

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

几乎没人敢用


C++11 的设计目标

noexcept 的核心动机是:

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

换句话说:

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


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

void f() noexcept;

并不是说:

“这个函数不会抛异常”

而是说:

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

即:

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

noexcept承诺,不是能力检测。


三、noexcept 的两种形式

1.无条件 noexcept

void f() noexcept;

等价于:

void f() noexcept(true);

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

template<typename T>void foo(T&& x) noexcept(noexcept(T(std::forward<T>(x))));

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

示例:完美转发构造

template<typename T>T make() noexcept(noexcept(T())) {return T();}

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

void f() noexcept;
void g();
using F = void(*)();
using NF = void(*)() noexcept;
F  pf = g;   // OK
NF pnf = f;  // OK

但:

NF pnf = g;  //  编译错误

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


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

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

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


2.STL 的核心决策依据

std::vector 扩容行为

if (T is noexcept-move-constructible)
使用 move
else
使用 copy

等价于:

std::is_nothrow_move_constructible_v<T>

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

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

这就是 STL 要求 move ctor noexcept 的原因


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

Rule of Five + noexcept

struct X {
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() {
throw std::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<typename T>void safe_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);}

九、noexcept vs const

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

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

误区 1:noexcept = 不会抛异常

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


错误理解示例

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

运行结果

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

对比:非 noexcept

void g() {
throw std::runtime_error("boom");
}
int main() {
try {
g();
} catch (const std::exception& e) {
std::cout << "caught: " << e.what() << '\n';
}
}

输出

caught: boom

正常异常语义


工程结论

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


误区 2:随便给函数加 noexcept

这是生产事故级错误


错误示例:包装函数

void may_throw() {
throw std::runtime_error("error");
}
void wrapper() noexcept {
may_throw();   // 
}
int main() {
wrapper();
}

运行结果

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

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

void log(const std::string& s) {
if (s.empty()) {
throw std::logic_error("empty");
}
}
void foo() noexcept {
log("");   // 间接抛异常
}

根本看不到 throw,却直接 terminate


正确写法 1:内部吞异常

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

正确写法 2:条件 noexcept

template<typename F>void call(F&& f) noexcept(noexcept(f())) {f();}

工程结论

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


误区 3:忘记给 move ctor 加 noexcept

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


错误示例

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

STL 的真实逻辑

if (is_nothrow_move_constructible<T>)moveelsecopy

结果

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

正确示例

struct Good {
Good() = default;
Good(const Good&) = default;
Good(Good&&) noexcept {}  // 
};

对比验证(可加日志)

struct Verbose {
Verbose() = default;
Verbose(const Verbose&) {
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
用户回调

不要使用

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

十二、一个完整工程示例

struct Pose {
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++ 性能、异常安全和库设计的核心支点

http://www.jsqmd.com/news/317205/

相关文章:

  • 【2026-01-28】连岳摘抄
  • 2025_11_7_刷题 - 实践
  • 聊聊高通量组织研磨器,性价比高的厂家有哪些
  • TNF-α (46-65) (human) ;NQLVVPSEGLYLIYSQVLFK
  • 2026年全国设备吊装上楼厂家权威榜单 全场景解决方案全景解析
  • 分体式吸鼻器品牌选购,吸鼻器服务商厂家哪家靠谱
  • 论文AI率90%怎么办?这5款工具帮你降到15%以下
  • 聊聊便携式OCT光谱仪,好用的品牌有哪些
  • 2026年湖北中医药大学中医师承学习班口碑解读,选哪家更靠谱
  • 知网AI率总是降不下来?试试这几款专业工具
  • 个人开发者软著申请指南:从零到拿证只需这4步
  • SCI论文降AI率哪家强?5款学术级工具对比推荐
  • docker镜像离线导出、导入
  • 公众号运营必看:5款去AI味工具让内容更自然
  • C++面向对象入门:实验二
  • SCI论文投稿前必备:6款降AI率工具实测推荐
  • 2026年医疗器械数控机床定制:从需求到落地的关键考量,自动化数控机床/双主轴数控车床,医疗器械数控机床批发推荐排行榜
  • 2026年湘潭电吉他正规供应商推荐,性价比高的品牌汇总
  • 2026年质量好的斗牛士电吉他品牌推荐与耐用性排名
  • 聊聊广东小型温控开关供应商,哪家性价比高
  • 聚氨酯瓦壳厂家哪家靠谱,奥强保温材料
  • 教育网站用帝国CMS批量上传Word试卷时,公式和图片会丢失吗?
  • AWS Knowledge BaseMCP
  • 2026年口碑好的模温机厂家推荐,支持精品定制服务
  • 2026年行业内排行前列的ISO认证公司选哪家,知识产权认证/ISO20000认证,ISO认证办理机构哪家强
  • 论文AI率90%降到10%以下,亲测这5款工具真的有效
  • 2026选购全自动封箱机,哪些制造厂家值得信赖?井字封箱机/包装机/封箱机/三维膜包装机,全自动封箱机生产商哪家靠谱
  • 2026年全国设备吊装上楼厂家哪家好?场景化选型与避坑指南 差异化分析
  • Brave Search MCP
  • Redis MCP