C++11-特性大全
一、语言核心特性
1、auto 类型推导
让编译器自动推断类型
auto x = 10; // int
auto it = v.begin(); // 容器迭代器
2、nullptr
替代 NULL / 0,类型安全
int* p = nullptr;
3、 范围 for 循环(range-based for)
for (auto& x : vec) {
cout << x << endl;
}
4.1右值引用
C++的右值引用(rvalue reference)是C++11引入的一个非常重要的特性,主要用来实现“移动语义”和“完美转发”,从而大幅提升程序的性能,尤其是涉及资源管理(比如动态内存、文件句柄等)
什么是右值引用
- 左值(lvalue):有名字,可以取地址的对象。比如变量名就是左值。
- 右值(rvalue):没有名字,通常是临时对象或字面值,比如
42、x + y的结果、函数返回的临时对象等。
在传统C++中,函数参数通常是通过值传递(拷贝)或引用传递(左值引用)来接收,但拷贝性能开销大。引入右值引用后,可以对临时对象进行“移动”,避免不必要的拷贝。
右值引用的语法
T&& x; // 右值引用
例如:
int&& a = 5; // 5是右值,a是右值引用,绑定临时对象5
4.2 移动语义
移动语义允许你“窃取”临时对象的资源,而不是拷贝。常见场景是自定义类的拷贝构造函数和移动构造函数。
#include <iostream>
#include <cstring>
class MyString {
char* data;
public:
// 构造函数
MyString(const char* str = "") {
data = new char[strlen(str) + 1];
strcpy(data, str);
std::cout << "Constructed: " << data << std::endl;
}
// 拷贝构造函数
MyString(const MyString& other) {
data = new char[strlen(other.data) + 1];
strcpy(data, other.data);
std::cout << "Copy Constructed: " << data << std::endl;
}
// 移动构造函数
MyString(MyString&& other) noexcept {
data = other.data; // 直接“偷”资源指针
other.data = nullptr; // 防止析构时释放资源
std::cout << "Move Constructed" << std::endl;
}
// 析构函数
~MyString() {
if(data) {
std::cout << "Destructing: " << data << std::endl;
delete[] data;
} else {
std::cout << "Destructing: null" << std::endl;
}
}
};
MyString createString() {
MyString temp("Temporary");
return temp; // 这里会调用移动构造
}
int main() {
MyString s1 = createString(); // 移动构造
//MyString s2 = s1; // 拷贝构造
return 0;
}
场景1
MyString createString() {
MyString temp("Temporary");
return temp; // 这里预期调用移动构造函数
}
int main() {
MyString s1 = createString(); // 这里期待调用移动构造
// MyString s2 = s1; // 注释掉了
}
为什么没有看到“Move Constructed”?
- 编译器优化(NRVO,返回值优化)
C++编译器通常会启用一种叫做 返回值优化(RVO) 或 命名返回值优化(NRVO) 的优化技术。
这意味着:
- 在
createString()里创建的局部对象temp,编译器会直接在s1的内存位置上构造,而不是先构造temp再移动构造s1。 - 这样就避免了额外的移动构造或拷贝构造,节省性能。
换句话说,temp和s1其实是同一个对象,没有调用移动构造函数。
你看到的输出
Constructed: Temporary
Destructing: Temporary
- “Constructed”是构造
temp(其实直接构造到s1空间了) - “Destructing”是程序结束时析构
s1,因为temp和s1是同一个对象 - 没有“Move Constructed”,说明移动构造被省略了
场景2
MyString createString() {
MyString temp("Temporary");
return std::move(temp); // 通过std::move强制转换成右值
}
这样写后,移动构造会被调用(一般不会被优化掉),控制台会输出“Move Constructed”。

4.3完美转发
完美转发(Perfect Forwarding)和右值引用密切相关,主要用来让函数模板把参数“原封不动”地转发给另一个函数,保持参数的值类别(左值还是右值)不变。
为什么需要完美转发?
假设你写了一个模板函数,想调用另一个函数并传递参数,但不知道传进来的参数是左值还是右值。普通的转发可能会导致参数丢失右值性质,从而调用不到对应的移动构造或者移动赋值等优化。
完美转发的关键是:
- 使用模板类型参数
T&&(这时T&&不是右值引用,而是转发引用,或称为万能引用 universal reference) - 使用
std::forward<T>(param)完美转发参数
demo
#include <iostream>
#include <utility>
// 普通函数,分别接收左值引用和右值引用
void func(int& x) {
std::cout << "func called with lvalue: " << x << std::endl;
}
void func(int&& x) {
std::cout << "func called with rvalue: " << x << std::endl;
}
// 转发函数模板
template<typename T>
void wrapper(T&& arg) {
// 完美转发给 func,保留左值/右值性质
func(std::forward<T>(arg));
}
int main() {
int a = 10;
wrapper(a); // 传入左值,func(int&)被调用
wrapper(20); // 传入右值,func(int&&)被调用
}
代码解释:
T&& arg是转发引用(万能引用),可以绑定左值和右值std::forward<T>(arg)保证了传进去的参数如果是左值就传左值,右值就传右值- 这样
func就能根据参数的值类别正确重载调用
总结
- 右值引用用于移动语义,绑定右值,避免拷贝,提高性能
- **转发引用(万能引用)**是模板中
T&&参数,能绑定任意值类别 - 完美转发利用
std::forward让参数“完美地”传递给下层函数,保持原始的左值/右值属性
为什么很多开发者没用过完美转发?
- 业务代码层面多是具体实现,不涉及通用模板库
许多日常业务开发里写的代码,比如业务逻辑、数据处理、UI交互等,更多的是写具体功能,不需要写高度通用和模板化的转发代码。 - 完美转发主要用于编写泛型库、框架或工具
例如标准库中的std::forward、std::make_unique、std::bind、std::thread启动函数包装等,都需要完美转发来做到参数的“零开销转发”。这类代码是框架层或库层的事情。 - 使用不当风险较高
完美转发如果用错(比如忘记用std::forward而用std::move或者直接传参),会破坏参数的值类别,导致性能下降或语义错误,不太适合初学者或者快速开发场景。
典型业务场景示例
- 泛型工厂函数
你想写个createObject函数模板,不论传入构造函数的参数是左值还是右值,都原封不动传递给对象构造函数。
template<typename T, typename... Args>
T* createObject(Args&&... args) {
return new T(std::forward<Args>(args)...);
}
- 事件回调、信号槽系统
当你写一个事件调度系统,需要将回调函数和参数以泛型方式传递,完美转发保证参数的高效传递和调用。 - 包装器、代理函数
写函数包装器(比如装饰器、日志、计时器)时,把参数完整地传给被包装函数,而不改变参数特性。
场景总结
- 完美转发特别适合写那些需要“透传参数”且不想损失效率和语义的通用模板代码。
- 如果你是业务开发者,可以暂时不用太纠结完美转发,但如果想写库、框架,或者追求极致性能和泛型代码,那么学会完美转发非常重要。
5、lambda表达式
auto f = [](int a, int b) {
return a + b;
};
更详细的可以参考我之前写的文章《C++的lambda表达式》:https://blog.csdn.net/weixin_63787588/article/details/148208016
6、constexpr
能在编译期算的,就不要拖到运行期
一、场景 1:编译期常量(替代 #define / const)
❌ 旧写法
#define N 100
✅ C++11 推荐
constexpr int N = 100;
int arr[N]; // 编译期确定
🔑 关键点
constexpr变量是真正的编译期常量- 比
const更强(const不一定是编译期)
二、场景 2:constexpr 函数(编译期计算)
Demo:编译期平方
constexpr int square(int x) {
return x * x;
}
int arr[square(4)]; // OK,数组大小 = 16
对比普通函数
int square2(int x) {
return x * x;
}
// int arr[square2(4)]; // ❌ 编译错误
🔑 关键点
- 参数是编译期常量 → 编译期执行
- 参数是运行期值 → 退化成普通函数
三、场景 3:用于 static_assert(非常常见)
Demo:编译期校验
constexpr int BufferSize = 1024;
static_assert(BufferSize % 2 == 0, "BufferSize must be even");
🔑 关键点
static_assert只能用编译期表达式constexpr是黄金搭档
四、场景 4:constexpr 构造函数(C++11)
Demo:编译期对象
struct Point {
int x, y;
constexpr Point(int a, int b) : x(a), y(b) {}
};
constexpr Point p(1, 2);
static_assert(p.x == 1, "x must be 1");
🔑 关键点
- 构造函数体必须很简单(C++11 要求更严格)
- 成员必须是 字面值类型
五、场景 5:枚举 / 配置 / 编译期策略
Demo:编译期参数
constexpr int MaxConnections(bool debug) {
return debug ? 10 : 100;
}
constexpr int maxConn = MaxConnections(false);
六、场景 6:模板元编程(高阶)
Demo:编译期斐波那契
constexpr int fib(int n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
static_assert(fib(10) == 55, "fib error");
七、场景 7:替代 magic number(工程实践)
❌ 不推荐
if (size > 4096) { }
✅ 推荐
constexpr size_t MaxPacketSize = 4096;
if (size > MaxPacketSize) { }
八、constexpr vs const(面试必问)
| 对比项 | const | constexpr |
|---|---|---|
| 是否编译期 | 不一定 | 一定 |
| 可用于数组大小 | ❌ | ✅ |
| 可用于模板参数 | ❌ | ✅ |
| 语义 | 只读 | 编译期常量 |
九、常见坑 ⚠️
❌ constexpr 函数不一定在编译期执行
int x;
cin >> x;
constexpr int y = square(x); // ❌
❌ C++11 限制多
constexpr int f(int x) {
int y = x + 1; // ❌ C++11 不允许局部变量
return y;
}
(C++14 之后放宽)
十、一句话总结(面试用)
constexpr 用于 声明编译期常量或可在编译期执行的函数,常见于数组大小、模板参数、static_assert、配置值和性能敏感代码中。
7、decltype
auto 用“值”推类型,decltype 用“表达式”推类型。
一、最核心区别(一定要记)
| 对比点 | auto | decltype |
|---|---|---|
| 推导依据 | 初始化的值 | 表达式本身 |
| 会不会丢引用 | 会 | 不会 |
| 会不会丢 cv | 会 | 不会 |
| 是否需要初始化 | 必须 | 不需要 |
| 常用于 | 变量声明 | 精确类型获取 |
二、场景 1:最基本用法
auto:看右值
int x = 10;
auto a = x; // int
decltype:看表达式
int x = 10;
decltype(x) b = x; // int
看不出区别?继续👇
三、场景 2:引用和 const(关键区别 ⭐)
int x = 10;
int& rx = x;
const int cx = x;
auto 会“脱衣服”
auto a1 = rx; // int
auto a2 = cx; // int
decltype 完全保留
decltype(rx) d1 = x; // int&
decltype(cx) d2 = x; // const int
👉 auto 默认按值推导,decltype 精确保留类型
四、场景 3:表达式规则(面试高频)
decltype 的核心规则
decltype(expr)
expr是 变量名 → 得到声明类型expr是 表达式 → 得到表达式类型(可能是引用)
Demo
int x = 10;
decltype(x) a = x; // int
decltype((x)) b = x; // int& ← 注意括号!
📌 双括号 = 左值表达式 = 引用
五、场景 4:函数返回类型(经典用法 ⭐)
❌ auto 做不到
template<typename T, typename U>
??? add(T a, U b);
✅ decltype
template<typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
(C++14 才能直接 auto add(...))
六、场景 5:STL / 泛型编程(非常常见)
获取容器元素类型
std::vector<int> v;
decltype(v)::value_type x; // int
推导迭代器解引用类型
auto it = v.begin();
decltype(*it) y = *it; // int&
七、场景 6:配合 auto(最佳实践)
auto it = v.begin();
decltype(it) it2 = v.end();
👉 auto 声明,decltype 复用
八、auto + decltype 实战对比
❌ 错误写法
auto get() { return x; } // C++11 不支持
✅ C++11 正确写法
auto get() -> decltype(x) {
return x;
}
九、面试总结模板(直接背)
auto 根据初始化表达式推导类型,会忽略引用和 cv;
decltype 根据表达式本身推导类型,能精确保留引用和 const,
常用于泛型编程、返回类型推导和复杂类型获取。
十、什么时候用谁?
- **用 **
**auto**
👉 简化变量声明(迭代器、lambda、复杂模板) - **用 **
**decltype**
👉 精确类型、模板返回值、引用语义不能丢时
8、using 类型别名
替代 typedef(更清晰):
using int64 = long long;
9、static_assert
编译期断言:
static_assert(sizeof(int) == 4, "int must be 4 bytes");
10、explicit 支持多参数构造
explicit 是干什么的?
一句话定义(面试版):
**explicit**** 用来禁止构造函数或转换函数发生“隐式类型转换”。**
它的目的只有一个:
👉 防止“看起来合法,其实有坑”的自动类型转换
最经典的隐式转换问题(不加 explicit)
问题代码(⚠️ 很危险)
class Foo {
public:
Foo(int x) {
cout << "Foo(int)" << endl;
}
};
void func(Foo f) {
}
int main() {
func(10); // ❗️这里发生了什么?
}
实际发生的事
10 → Foo(10) → func(Foo)
👉 编译器悄悄帮你做了构造函数调用
输出:
Foo(int)
问题在哪?
func明明要的是Foo- 却能传一个
int - 调用者根本意识不到发生了对象构造
explicit 如何解决这个问题?
正确写法(加 explicit)
class Foo {
public:
explicit Foo(int x) {
cout << "Foo(int)" << endl;
}
};
void func(Foo f) {
}
int main() {
// func(10); // ❌ 编译错误
func(Foo(10)); // ✅ 必须显式构造
}
结果
- ❌ 隐式转换被禁止
- ✅ 调用者必须明确表达意图
explicit 的本质规则
哪些构造函数会触发隐式转换?
单参数构造函数
Foo(int x); // 会触发
Foo(int x, int y); // 不会
explicit 作用的真实规则
隐式允许的情况(没 explicit)
Foo f = 10; // int → Foo
func(10); // int → Foo
explicit 禁止的情况
explicit Foo(int x);
Foo f = 10; // ❌
func(10); // ❌
Foo f(10); // ✅
explicit + 多参数构造(C++11 新增)
C++11 之前(explicit 只能修饰单参)
class Foo {
public:
explicit Foo(int x, int y); // ❌ C++98 不允许
};
C++11 之后(合法)
class Foo {
public:
explicit Foo(int x, int y) {
cout << "Foo(int, int)" << endl;
}
};
作用场景
Foo f1(1, 2); // ✅
Foo f2 = {1, 2}; // ❌ 禁止隐式列表初始化
explicit 与 initializer_list 的经典坑
没有 explicit(容易踩雷)
class Foo {
public:
Foo(int x) { }
};
Foo f = {10}; // 隐式转换
加 explicit
class Foo {
public:
explicit Foo(int x) { }
};
Foo f = {10}; // ❌ 编译错误
Foo f(10); // ✅
explicit 还能修饰什么?(C++11)
转换运算符(非常重要)
class Foo {
public:
explicit operator bool() const {
return true;
}
};
效果对比
Foo f;
if (f) { } // ✅ 允许(上下文转换)
bool b = f; // ❌ 禁止
👉 防止对象被随意当成 bool/int 使用
explicit 的工程级使用建议
什么时候一定要加?
- 单参数构造函数
- 表示“语义转换”的构造函数
- 配置类 / 资源类 / 句柄类
什么时候可以不加?
- 明确是“数值包装”的类型(如
std::chrono内部) - 你非常确定隐式转换是安全且合理的
二、面向对象 & 语法增强
11、override / final
override
重写
class Base {
virtual void foo() = 0;
};
class Derived : public Base {
void foo() override;
};
一、final 是干什么的?
final 用来禁止继承或禁止虚函数被重写,主要解决两个问题:
- 防止类被继承
- 防止虚函数被继续 override
一句话:
final = “到此为止,不准再改”
二、final 修饰类:禁止被继承
经典 Demo
class Base final {
};
class Derived : public Base { // ❌ 编译错误
};
作用
- 明确设计意图:这个类不允许作为基类
- 常见于:
- 工具类
- RAII 类
- 框架内部实现类
三、final 修饰虚函数:禁止子类重写
经典 Demo
class Base {
public:
virtual void foo() final {
}
};
class Derived : public Base {
public:
void foo() override { // ❌ 编译错误
}
};
作用
- 防止子类破坏基类行为
- 保证关键逻辑不可被修改
四、final 和 override 的关系(高频面试点)
正确用法(强烈推荐)
class Derived : public Base {
public:
void foo() override final;
};
含义
override:我在重写父类虚函数final:你们不能再重写我
👉 常见在 中间层类 中
五、为什么需要 final?(真实问题)
没有 final 的风险
class Base {
public:
virtual void process() {
step1();
step2();
}
};
子类可能这样:
class BadDerived : public Base {
public:
void process() override { // 破坏流程
step2();
step1();
}
};
用 final 保护
class Base {
public:
virtual void process() final {
step1();
step2();
}
};
六、final 带来的额外好处:性能优化
编译器可做的优化
final虚函数 → 可去虚表- 调用可被 内联
- 减少间接调用开销
👉 在性能敏感代码(游戏 / 引擎 / 高频调用)很常见
七、final vs const(别混)
| 关键字 | 作用对象 | 含义 |
|---|---|---|
final | 类 / 虚函数 | 禁止继承 / 重写 |
const | 成员函数 / 变量 | 不允许修改 |
八、final 的常见误用 ⚠️
❌ 用在非虚函数上
void foo() final; // ❌ 没意义(C++直接禁止)
❌ 滥用
class EverythingFinal final { };
会导致:
- 扩展性差
- 测试 / mock 困难
九、什么时候该用 final?(工程经验)
✅ 推荐使用:
- 框架底层类
- 不希望被继承的工具类
- 关键算法函数
- 性能敏感的虚函数
❌ 不推荐:
- 业务层可扩展类
- 明确需要多态扩展的接口
十、面试标准回答(背这个)
final 用于禁止类被继承或禁止虚函数被重写,可以明确设计意图、防止误用,同时帮助编译器做性能优化,常与 override 配合使用。
12、默认/删除函数
Foo() = default;
Foo(const Foo&) = delete;
13、委托构造函数
Foo(int x) : Foo(x, 0) {}
14、enum class(强类型枚举)
enum class Color { Red, Green, Blue };
三、STL 和库增强
15、智能指针
std::unique_ptrstd::shared_ptrstd::weak_ptr
可以参考之前写的文章:https://blog.csdn.net/weixin_63787588/article/details/134458428?spm=1001.2014.3001.5501
16、新容器
std::arraystd::unordered_mapstd::unordered_set
17、std::function / std::bind
std::function<int(int)> f;
18、正则表达式
std::regex r("\\d+");
19、时间库 <chrono>
std::chrono::steady_clock::now();
20、线程库
std::thread t(func);
std::mutex m;
std::lock_guard<std::mutex> lock(m);
四、模板相关增强
21、变参模板
template<typename... Args>
void func(Args... args);
22、std::initializer_list
vector<int> v = {1, 2, 3};
23、SFINAE + decltype 改进
一、先快速理解:SFINAE 是在解决什么问题?
SFINAE(Substitution Failure Is Not An Error) 的核心作用是:
在模板实例化时,如果某个类型推导失败,不报错,而是“自动忽略该模板”
这使得我们可以:
- 根据 类型是否支持某个操作
- 有 / 没有某个成员函数
- 表达式是否合法
来选择不同的模板版本。
二、为什么要引入 decltype?
2.1 传统 SFINAE 的问题
老写法通常是这样(C++11 之前或早期):
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type
func(T t) {}
问题:
- 只能判断「类型特性」(如是不是整数)
- 无法判断「某个表达式是否成立」
- 写法冗长,可读性差
2.2 decltype 的优势
decltype(expr) 能做到一件关键的事:
把“表达式是否能编译”变成一个类型推导问题
如果 expr不合法:
decltype(expr)推导失败- 触发 SFINAE
- 模板被自动排除
这正是 SFINAE 最想要的能力。
三、最经典的用法:判断表达式是否存在
3.1 基本模式(核心模板)
template<typename T>
auto func(T t) -> decltype(t.size(), void())
{
// 只有 T 有 size() 才能进来
}
发生了什么?
t.size()只是用于检测是否合法, void()确保返回类型是void- 如果
T没有size()→ 推导失败 → SFINAE 生效
3.2 配合兜底版本(推荐写法)
template<typename T>
auto func(T t) -> decltype(t.size(), void())
{
std::cout << "has size(): " << t.size() << "\n";
}
template<typename T>
void func(T)
{
std::cout << "no size()\n";
}
使用效果
func(std::vector<int>{1,2,3}); // has size()
func(10); // no size()
四、decltype + SFINAE 的改进写法(比 enable_if 好)
4.1 利用返回类型推导(C++14 起)
template<typename T>
auto func(T t)
{
if constexpr (requires { t.size(); })
{
std::cout << t.size();
}
}
但如果不用 requires(C++20 之前),decltype 是主力。
4.2 常见改进套路:检测成员函数
template<typename T>
auto print(const T& t) -> decltype(t.toString(), void())
{
std::cout << t.toString();
}
template<typename T>
void print(const T&)
{
std::cout << "no toString()\n";
}
适合场景:
- 日志系统
- 通用调试输出
- 框架代码(适配用户类型)
五、结合 std::declval(最常用组合)
5.1 为什么要 declval?
有些类型:
- 不可构造
- 构造代价很高
std::declval<T>() 可以:
- 在不创建对象的情况下
- 假装有一个
T
5.2 标准写法(工业级)
template<typename T>
auto func(int) -> decltype(std::declval<T>().size(), void())
{
std::cout << "has size\n";
}
template<typename T>
void func(...)
{
std::cout << "no size\n";
}
这是一个完整的 SFINAE 分发技巧
int版本优先- 推导失败才会落到
...
六、典型使用场景总结
6.1 通用库 / 框架代码
- STL 风格算法
- 序列化 / 反序列化
- 打印、日志、调试工具
6.2 类型适配(鸭子类型)
// 只关心“能不能这样用”,不关心继承
obj.size();
obj.begin();
obj.end();
6.3 编译期能力检测
- 判断是否支持
operator<< - 判断是否支持
push_back - 判断是否可比较
七、什么时候不该用 SFINAE + decltype?
7.1 能用 Concepts / requires 时
如果你用的是 C++20+:
template<typename T>
requires requires(T t) { t.size(); }
void func(T t) {}
👉 更清晰、更安全
八、一句话总结
**SFINAE + decltype 的核心价值是:
**把“表达式是否合法”变成模板选择条件
它最适合:
- 泛型库
- 类型无关代码
- 编译期能力检测
五、其他实用特性
24、原始字符串
std::string s = R"(C:\path\file.txt)";
25、noexcept
一、noexcept 是干什么的?(一句话版)
**noexcept**** 用来告诉编译器:这个函数“保证不会抛异常”**
它影响三件事:
- **程序是否直接 **
**std::terminate** - 标准库是否选择这个函数
- 编译器能不能做更激进的优化
二、最基本用法
2.1 标注函数不会抛异常
void f() noexcept
{
// 不允许抛异常
}
如果 f() 内部真的抛了异常:
throw std::runtime_error("err"); // ❌
👉 程序立刻调用 **std::terminate()**,不会被 catch
2.2 对比不写 noexcept
void g()
{
throw 1;
}
try {
g();
} catch (...) {
// 能捕获
}
三、条件式 noexcept(重点)
3.1 语法
noexcept(表达式)
**如果表达式在编译期为 true → noexcept
**否则 → 可能抛异常
3.2 典型写法(模板必学)
template<typename T>
void func(T& t) noexcept(noexcept(t.swap(t)))
{
t.swap(t);
}
含义:
- 如果
T::swap是noexcept - 那
func也是noexcept - 否则就不是
四、noexcept 是如何参与重载选择的?
4.1 noexcept 是函数类型的一部分(C++17)
void f() noexcept;
void f(); // 不同类型
👉 但 不能只靠 noexcept 重载
void f() noexcept {}
void f() {} // ❌ 重定义
4.2 标准库为什么在乎 noexcept
std::vector 移动构造
std::vector<T> v2 = std::move(v1);
规则是:
**只有当 **T** 的移动构造是 **noexcept**
**vector 才会用移动,否则用拷贝
struct A {
A(A&&); // 可能抛异常
};
struct B {
B(B&&) noexcept; // 不抛异常
};
👉 vector<B> 扩容时更快、更安全
五、noexcept 运算符(容易混)
5.1 这是一个编译期运算符
noexcept(expr)
返回:
true:expr 不会抛异常false:可能抛异常
static_assert(noexcept(42));
static_assert(!noexcept(throw 1)); // 永远 false
5.2 和函数声明里的 noexcept 不同
void f() noexcept; // 承诺
bool b = noexcept(f()); // 查询
六、什么时候必须写 noexcept?
6.1 移动构造 / 移动赋值(强烈建议)
struct X {
X(X&&) noexcept;
X& operator=(X&&) noexcept;
};
否则:
vector<X>性能下降- 某些容器直接退化成拷贝
6.2 析构函数(隐式 noexcept)
~X(); // 默认是 noexcept(true)
你不能让析构抛异常:
~X() noexcept(false); // ❌ 非常危险
七、什么时候不该写 noexcept?
7.1 你无法保证不抛异常时
void log()
{
std::string s;
s += "xxx"; // 可能分配失败
}
❌ 不要乱加 noexcept
7.2 接口层 / 业务层
- 框架底层:多用
noexcept - 业务逻辑:让异常自然传播更好
八、noexcept vs throw()(旧)
void f() throw(); // C++11 以前
- 已废弃
- 行为接近
noexcept(true) - 不要再用
九、一句话总结
**noexcept**** 是“异常契约” + “优化提示”**
记住三点就够:
- 承诺不抛 → 抛了就 terminate
- 模板里要用条件 noexcept
- 移动构造一定要 noexcept
26、对齐控制
alignas(16) int x;
更多推荐


所有评论(0)