> 本文档从 C++ 中文周刊(第1期~第196期)中提炼的编码规范、设计模式、性能优化和避坑指南。 > 适用于日常 C++ 开发参考。 ---
Scanned 9/12/2026
Install to Claude Code
npx -y skills add aibot88/sec_skill_store --skill wanghenshui-cppweeklynews-. --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Wanghenshui Cppweeklynews .?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-wanghenshui-cppweeklynews)More formats (shields.io, HTML) on the badges page.
# C++ 开发技能指南
> 本文档从 C++ 中文周刊(第1期~第196期)中提炼的编码规范、设计模式、性能优化和避坑指南。
> 适用于日常 C++ 开发参考。
---
## 一、现代 C++ 编码规范
### 1.1 优先使用 range-based for 和 Ranges 库
传统 for 循环"过于灵活",容易引入 off-by-one、修改错误变量等 bug。编译器无法防止这些问题。
```cpp
// ❌ 经典错误
for (auto i = 0; i <= vec.size(); ++i) // 应该是 <
use(vec[i]);
for (auto i = vec.size() - 1; i >= 0; --i) // 无符号数永远 >= 0,死循环!
use(vec[i]);
// ✅ 现代写法
for (auto const& rec : records)
use(rec);
// 反向迭代(C++20)
for (auto const& rec : std::views::reverse(records))
use(rec);
// 带索引迭代(C++23)
for (auto [i, rec] : std::views::enumerate(records))
use(i, rec);
// 多序列同时迭代(C++23)
for (auto [name, rec] : std::views::zip(names, records))
use(name, rec);
```
*来源:第190期*
### 1.2 用 `std::source_location` 替代 `__FILE__` / `__LINE__` 宏
```cpp
// ❌ 传统宏方法
#define ASSERT(cond, msg) Assert(cond, msg, __FUNCTION__, __LINE__)
// ✅ C++20
void Assert(bool condition, std::string_view msg,
std::source_location loc = std::source_location::current()) {
if (!condition) {
std::clog << loc.function_name() << ':' << loc.line() << ": " << msg << '\n';
}
}
// 调用时不需要宏:
Assert(1 != 2, "Not met");
```
关键:`std::source_location::current()` 作为默认参数,在调用侧求值。
*来源:第190期*
### 1.3 `constexpr` + `consteval` 双路径设计
```cpp
consteval size_t strlen_ct(const char* s) { // 纯编译期
size_t n = 0;
for (; s[n] != '\0'; ++n);
return n;
}
size_t strlen(const char* s); // 纯运行期
constexpr size_t strlen_dual(const char* s) { // 双路径
if consteval {
return strlen_ct(s); // 编译期路径
} else {
return strlen(s); // 运行期路径
}
}
```
`constexpr` 函数最好两种分支都实现,避免意外问题。
*来源:第150期*
### 1.4 用 `std::expected` 替代异常做错误处理(C++23)
```cpp
std::expected<int, std::string> convertToInt(const std::string& input) {
int value{};
auto [ptr, ec] = std::from_chars(input.data(), input.data() + input.size(), value);
if (ec == std::errc())
return value;
if (ec == std::errc::invalid_argument)
return std::unexpected("Invalid number format");
if (ec == std::errc::result_out_of_range)
return std::unexpected("Number out of range");
return std::unexpected("Unknown conversion error");
}
```
*来源:第150期*
### 1.5 Concepts 和 `requires` 的正确用法
```cpp
// 基本 requires
template <typename T>
requires std::integral<T>
auto debug_output(const T& t);
// requires requires(检测成员函数)
template <typename T>
requires requires(const T& t) { t.debug_output(); }
auto debug_output(const T& t);
// if constexpr + requires(编译期检测能力)
template <typename Cont, typename Rng>
void cont_assign(Cont& cont, Rng&& rng) {
cont.clear();
if constexpr (requires { cont.reserve(std::ranges::size(rng)); }) {
cont.reserve(std::ranges::size(rng));
}
for (auto&& elem : std::forward<Rng>(rng)) {
cont.push_back(std::forward<decltype(elem)>(elem));
}
}
```
*来源:第170期*
### 1.6 `deducing this` 消除成员函数重复(C++23)
```cpp
// ❌ 传统写法:4个重载
struct Foo {
void bar() &;
void bar() &&;
void bar() const &;
void bar() const &&;
};
// ✅ C++23
template <typename T>
class Optional {
template <typename Self>
constexpr auto operator->(this Self&& self) {
return addressof(self.m_value);
}
};
```
*来源:第50期*
### 1.7 inline namespace 做版本控制
```cpp
namespace gem {
inline namespace v1 {
struct Point { int x; int y; };
}
namespace v2 {
struct Point { int y; int x; }; // v2 改了布局
}
}
// 默认用 v1,需要时显式 gem::v2::Point
```
*来源:第160期*
### 1.8 小对象直接传值,不要 const T&
`string_view`、`span`、`int`、`chrono::duration` 等小对象,直接传值比传引用更高效。
*来源:第50期*
---
## 二、性能优化指南
### 2.1 火焰图驱动优化
RocksDB 优化案例(180s → 7.8s,23倍加速),每步用火焰图验证:
1. **Transaction Put → SST Writer**(180s → 19.5s):消除锁和排序开销
2. **关掉导入阶段不需要的过滤器和压缩**(19.5s → 14.3s)
3. **fast_float 替换 sscanf**(14.3s → 12s,16%提速)
4. **std::string → vector\<char\>**(12s → 10.6s):消除 null terminator 维护开销
5. **去掉热路径的运行期检查**(10.6s → 8.7s)
6. **消除 key 的隐藏拷贝**(8.7s → 7.8s)
**Key takeaways:**
- 避免热路径中的虚函数
- 别不必要地拷贝字符串
- 运行期检查能改 assert 就改 assert
*来源:第196期*
### 2.2 编译器比你聪明,不要手动"优化"
编译器将代码转换为 IR,相同操作的不同实现会被转换为规范形式。多种计算加法的方式(循环、递归、复杂逻辑)都会编译为单个 ARM 指令 `add w0, w1, w0`。
常量乘法也一样:手动优化 `522` 为 `(x << 9) + (x << 3) + (x << 1)`,编译器仍然会恢复为 `imul`。
**优先考虑代码清晰性而不牺牲性能。**
*来源:第190期*
### 2.3 `__builtin_unreachable()` 消除分支
```cpp
uint8_t sum_with_constraints(const uint8_t *data, size_t len) {
if (len % 32 != 0) __builtin_unreachable(); // len 一定是32的倍数
if (len == 0) __builtin_unreachable(); // len 一定非零
return std::accumulate(data, data + len, uint8_t(0));
}
```
注意:把 `data*` 换成 `vector`,gcc 下可能不能优化。
*来源:第180期*
### 2.4 TLS 性能优化清单
thread_local 对象在有类构造函数 + `-fPIC` 共享库时需要额外调用 `__tls_get_addr`,成为性能瓶颈。
优化指南:
- TLS 对象尽可能合并
- 不要为 TLS 写构造函数(用 trivial 类型)
- 频繁访问的对象用 `__attribute__((visibility("hidden")))`
- 关键变量用 `__attribute__((tls_model("initial-exec")))`
- 非共享库不要用 `-fPIC`
- 考虑 `-mtls-dialect=gnu2`
*来源:第180期*
### 2.5 `-O3 -flto` 是免费午餐
Redis 测试:`-O3 -flto` 性能至少提升 5%。PGO(Profile-Guided Optimization)值得进一步研究。
*来源:第100期*
### 2.6 低延迟编程:减少分支和跳转
- 勤用 `&&` `||` 利用短路特性
- 关注能生成 `cmov` 的写法(三元表达式、简单 if 赋值)
- 减少虚函数使用(但 `variant` + `visit` 某些场景比虚函数好)
- 善用 `[[gnu::always_inline]]` / `__builtin_expect`
- 字符串比较的 if-else 链改 switch
*来源:第150期*
### 2.7 查表法替代除法/取模
除法指令很慢。整数转字符串(itoa)用查表法替代循环除 10:
```cpp
static constexpr char radix_100_table[] = {
'0', '0', '0', '1', '0', '2', /* ... */
};
// 每次除100,查表取两位数字,速度翻倍
```
*来源:第50期*
### 2.8 SIMD 更节能
只要你的代码足够快,即使是 SIMD 这种费电的指令,整体能耗反而更少(因为执行时间短得多)。
*来源:第150期*
### 2.9 缓存友好的内存布局(SOA vs AOS)
AOS(Array of Structures)→ SOA(Structure of Arrays)转换可以显著提升缓存命中率,特别是在只访问部分字段时。
*来源:第180期*
### 2.10 循环优化:fission + interleave
- **Loop fission**:拆分包含不同数据依赖的循环,降低数据 buffer 大小使其小于 L1 cacheline
- **Interleave**:将不同任务交错分发,隐藏指令依赖链的延迟
- 用 [likwid](https://github.com/RRZE-HPC/likwid) 定位内存子系统瓶颈
*来源:第150期*
---
## 三、安全编码与 UB 避免
### 3.1 UB 的 43 条错觉(精选)
编译器的保证列表是空的。一旦有 UB,所有行为都是合规的:
- UB **不只**在 `-O2/-O3` 才触发
- UB 的影响**不局限于** UB 之后的代码 — UB 可以"时间旅行"
- UB **不保证**只是崩溃或死循环
- 相同二进制 + 相同输入重复跑,行为**不一定**一样
- 编译没报错**不代表**没有 UB
- ODR 违规是编译期/链接期 UB
*来源:第196期*
### 3.2 有符号位移陷阱
```cpp
// 64位机器上:
unsigned long set_bit_a(int bit) { return 1 << bit; } // 1 << 31 = 0xffffffff80000000 !!!
unsigned long set_bit_b(int bit) { return 1U << bit; } // 1U << 31 = 0x80000000 ✅
```
`1 << 31` 是有符号溢出(UB),符号扩展到 64 位时结果出乎意料。始终用 `1U` 或 `1ULL`。
*来源:第150期*
### 3.3 死循环也是 UB
```cpp
int main() {
while(1) ; // 无副作用的死循环是 UB
}
void unreachable() {
std::cout << "hello world\n"; // clang 会打印这个!
}
```
编译器把无副作用的死循环优化掉,没有 ret,直接跳到下一个函数。
*来源:第100期*
### 3.4 整数溢出没有银弹
C++ 没有内建的溢出检查。现有方案:
- 自定义类型加边界检查
- Rust 风格的 `overflowing_add` / `overflowing_mul`
- Chromium 的 [checked math](https://github.com/chromium/subspace)
- `-ftrapv` 编译选项(性能损失大)
*来源:第140期*
### 3.5 浮点数比较
```cpp
bool cmpEq(double a, double b,
double epsilon = 1e-7, double abstol = 1e-12) {
if (a == b) return true; // 处理 inf
double diff = std::fabs(a - b);
double reltol = std::max(std::fabs(a), std::fabs(b)) * epsilon;
return diff < reltol || diff < abstol;
}
```
或直接用 Boost.Test 的浮点比较实现。
*来源:第100期*
### 3.6 用 `constexpr` 在编译期捕获 UB
编译器在 constexpr 求值时必须拒绝 UB。可以利用这一点写编译期测试来捕获 UB。
*来源:第150期*
### 3.7 `std::uintptr_t` 处理地址
涉及地址转换时用 `std::uintptr_t` 而不是 `uint32_t`/`uint64_t`,避免 `reinterpret_cast` 的 `-fpermissive` 报错和平台兼容性问题。
*来源:第150期*
---
## 四、设计模式与惯用法
### 4.1 CRTP 表达式模板(延迟求值)
用于 Eigen 风格的数学库,避免中间临时对象:
```cpp
template <typename E>
class VecExpression {
public:
double operator[](size_t i) const {
return static_cast<E const&>(*this)[i];
}
};
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>> {
const E1& _u; const E2& _v;
public:
double operator[](size_t i) const { return _u[i] + _v[i]; }
};
// a + b + c 的类型是 VecSum<VecSum<Vec, Vec>, Vec>
// 赋值时才实际计算:elems[i] = a[i] + b[i] + c[i]
```
*来源:第170期*
### 4.2 防止对象切片
```cpp
// C++20 方案:CRTP + concepts
template <typename T>
struct DontSlice {
DontSlice() = default;
DontSlice(const std::derived_from<T> auto&) = delete;
DontSlice& operator=(const std::derived_from<T> auto&) = delete;
DontSlice(std::derived_from<T> auto&&) = delete;
DontSlice& operator=(std::derived_from<T> auto&&) = delete;
};
struct Base : DontSlice<Base> {
int x_;
Base(int x) : x_(x) {}
using DontSlice<Base>::DontSlice;
};
```
*来源:第190期*
### 4.3 RAII scope_exit
```cpp
template<class L>
class AtScopeExit {
L& m_lambda;
public:
AtScopeExit(L& action) : m_lambda(action) {}
~AtScopeExit() noexcept(false) { m_lambda(); }
};
// 使用宏简化
#define Auto(...) Auto_INTERNAL2(__COUNTER__, __VA_ARGS__)
// 用法
bool Mutate(State *state) {
state->DisableLogging();
Auto(state->EnableLogging());
if (!state->AttemptOperation1()) return false;
return true;
}
```
*来源:第150期*
### 4.4 `std::variant` 状态机
```cpp
using State = std::variant<Idle, Running, Error>;
// 状态转换用 std::visit
State transition(State current, Event event) {
return std::visit(overloaded{
[](Idle, StartEvent) -> State { return Running{}; },
[](Running, ErrorEvent e) -> State { return Error{e.msg}; },
[](auto s, auto) -> State { return s; }, // 默认:保持不变
}, current, event);
}
```
*来源:第120期*
### 4.5 类型安全的字典(Tag Dispatch)
```cpp
using dict = std::map<std::type_index, std::any>;
template <class Name, class T>
struct key final { explicit key() = default; };
template <class Name, class T>
auto get(const dict& d, key<Name, T> k) -> std::optional<T> {
if (auto pos = d.find(typeid(k)); pos != d.end())
return std::any_cast<T>(pos->second);
return std::nullopt;
}
// 声明 key(类只需声明不需定义)
using age_k = key<struct _age_, int>;
using name_k = key<struct _name_, std::string>;
constexpr inline auto age = age_k{};
constexpr inline auto name = name_k{};
```
注意:`typeindex` 计算较慢,大规模使用考虑字符串 hash 替代。
*来源:第140期*
### 4.6 transform_iterator 延迟计算
```cpp
template <typename Iter, typename Func>
class transform_iterator {
Iter it; Func func;
public:
using value_type = std::invoke_result_t<Func,
typename std::iterator_traits<Iter>::value_type>;
transform_iterator& operator++() { ++it; return *this; }
value_type operator*() const { return func(*it); }
bool operator!=(const transform_iterator& o) const { return it != o.it; }
};
// 类似 range,延迟计算。C++20 有 views::transform 更好用
```
*来源:第180期*
---
## 五、并发与异步
### 5.1 C++20 协程最小实现
协程三要素:
- **Promise**:存储异步状态和回调
- **Awaiter**:实现 `await_ready` / `await_suspend` / `await_resume`
- **promise_type**:通过 `coroutine_traits` 特化挂载协程语义
```cpp
class Awaiter {
Promise& m_promise;
public:
bool await_ready() { return m_promise.IsReady(); }
void await_suspend(std::coroutine_handle<> handle) {
m_promise.AddCallback([handle]() { handle.resume(); });
}
void await_resume() {}
};
```
实际项目推荐用 Boost.Asio 或 cppcoro,不要自己造轮子。
*来源:第196期*
### 5.2 线程安全的编译期保证
用零开销 Token 类型在编译期保证只在主线程调用:
```rust
pub struct MainThreadToken { _marker: PhantomData<*mut ()> }
// 需要 token 的函数签名
pub fn drain(&self, _token: MainThreadToken) { ... }
```
C++ 端用标记宏区分线程安全属性:
```cpp
#define SYNC /* 线程安全的 const 方法 */
#define UNSYNC /* 线程不安全的 const 方法 */
```
*来源:第195期*
### 5.3 TLS 对象的析构顺序
- `thread_local` 对象析构顺序不确定
- 析构中访问已销毁的 TLS 对象是 UB
- 非 trivial 的 TLS 对象在 `-fPIC` 下有额外性能开销
*来源:第180期*
### 5.4 锁内析构注意死锁
持有锁时销毁对象,如果对象的析构函数也需要加锁,就会死锁。特别注意 `shared_ptr` 的引用计数归零时机。
### 5.5 `std::move` 不保证移动
`std::move` 只是强制转换为右值引用:
- 如果接收方没有移动构造函数,仍然会拷贝
- `const T&&` 不会触发移动
- 能返回值优化(RVO/NRVO)时**不要** `std::move`
*来源:第140期*
---
## 六、编译器与工具链
### 6.1 编译时间分析
- Clang:`-ftime-trace` 生成火焰图
- GCC:配合 [ClangBuildAnalyzer](https://github.com/aras-p/ClangBuildAnalyzer)
- Visual Studio 17.9+:内置编译时间分析、字段内存布局分析、include 分析
- xmake:参考 xmake 的编译分析功能
*来源:第150期*
### 6.2 `constinit` 解决静态初始化顺序问题
```cpp
constinit int global_value = 42; // 保证编译期初始化
```
避免 Static Initialization Order Fiasco。能用就用。
*来源:第120期*
### 6.3 Sanitizer 使用指南
| Sanitizer | 编译选项 | 用途 |
|-----------|---------|------|
| ASan | `-fsanitize=address` | 内存越界、use-after-free |
| UBSan | `-fsanitize=undefined` | 未定义行为 |
| TSan | `-fsanitize=thread` | 数据竞争 |
| MSan | `-fsanitize=memory` | 未初始化内存 |
VS 的 ASan 支持 `continue_on_error` 模式。Debug 下 Sanitizer 可能很慢。
*来源:第100期、第120期、第180期*
### 6.4 编译期越界检查
```cpp
[[gnu::error("out-of-bounds access detected")]]
void static_bounds_check_failed();
template <typename Index>
void bounds_check(Index idx, Index limit) {
if (__builtin_constant_p(idx) && __builtin_constant_p(limit)) {
if (idx < Index{0} || idx >= limit)
static_bounds_check_failed(); // 编译错误
}
}
```
注意 `__builtin_constant_p` 不是 100% 可靠。
*来源:第140期*
### 6.5 C++20 模块
三大标准库实现(libstdc++、libc++、STL)都支持在 C++20 模式使用 `std` 模块,不需要 C++23。C++23 提供的 `std` 模块体验更好。
*来源:第190期*
---
## 七、常见陷阱与避坑
### 7.1 `std::accumulate` 的初始值类型
```cpp
std::vector<double> v = {1.5, 2.5, 3.5};
auto sum = std::accumulate(v.begin(), v.end(), 0); // ❌ 结果是 int!得到 6
auto sum = std::accumulate(v.begin(), v.end(), 0.0); // ✅ 得到 7.5
```
### 7.2 头文件中不要 `using namespace`
放在 `.cpp` 文件或函数作用域内。头文件中的 `using namespace` 会污染所有包含者。
*来源:第160期*
### 7.3 `fseek` 内部可能有 `mmap`
某些 glibc 版本中 `fseek` 内部使用 `mmap` 分配 buffer,在高并发场景下 `mmap_sem` 互斥锁冲突成为瓶颈。新版本 glibc 已修复。
*来源:第140期*
### 7.4 WebGL 缓存失效
循环中交替更新顶点/索引数据会触发浏览器缓存反复失效重建。解决方案:批处理合并绘制调用,或把所有 buffer 更新放在所有绘制调用之前。
*来源:第195期*
### 7.5 GPU 基准测试必须固定时钟频率
GPU 动态频率调整可导致基准测试结果 5-6 倍的波动。用 `SetStablePowerState` 或 RAII 封装固定频率。
*来源:第195期*
### 7.6 `std::string` vs `vector<char>` 在热路径
`std::string` 每次 `append` 维护 null terminator 有额外开销。热路径中单字节追加场景用 `vector<char>` 可提速 12%。
*来源:第196期*
### 7.7 整数加法近似浮点乘法
浮点数的指数-尾数结构使得整数加法可以近似浮点乘法,误差约 7%:
```cpp
float rough_float_multiply(float a, float b) {
constexpr uint32_t bias = 0x3f76d000;
uint32_t ai = bit_cast<uint32_t>(a), bi = bit_cast<uint32_t>(b);
return ai & bi ? bit_cast<float>(ai + bi - bias) : 0.0f;
}
```
仅用于对精度要求不高的快速计算场景。
*来源:第180期*
### 7.8 Windows 函数开头的 `MOV EDI, EDI`
这是 2 字节 NOP,用于在线热补丁:可以把这行替换成 `JMP` 到补丁代码。
*来源:第160期*
---
## 八、推荐资源
### 博客
- [johnnysswlab.com](https://johnnysswlab.com/) — 性能调优专家
- [lemire.me](https://lemire.me/blog/) — SIMD / 数据处理性能
- [devblogs.microsoft.com/oldnewthing](https://devblogs.microsoft.com/oldnewthing/) — Raymond Chen
- [quuxplusone.github.io](https://quuxplusone.github.io/blog/) — 语言律师级 C++ 分析
- [sandordargo.com](https://www.sandordargo.com/blog/) — 现代 C++ 特性系列
- [andreasfertig.com](https://andreasfertig.com/blog/) — C++ Insights 作者
- [biowpn.github.io](https://biowpn.github.io/) — 现代 C++ 实践
### 安全规范
- [360 安全规则集合](https://github.com/Qihoo360/safe-rules/) — UB 和安全编码规范
- [C++ Core Guidelines](https://isocpp.github.io/CppCoreGuidelines/) — 官方指南
### 工具
- [Compiler Explorer](https://godbolt.org/) — 在线查看汇编
- [quick-bench.com](https://quick-bench.com/) — 在线基准测试
- [rr-debugger](https://github.com/rr-debugger/rr) — 录制回放调试
- [Clang-Tidy](https://clang.llvm.org/extra/clang-tidy/) — 静态分析
- [PVS-Studio](https://pvs-studio.com/) — 静态分析
- [likwid](https://github.com/RRZE-HPC/likwid) — 性能计数器分析
### 常用开源库
- [fast_float](https://github.com/fastfloat/fast_float) — 快速浮点数解析
- [fmt](https://github.com/fmtlib/fmt) — 现代格式化库
- [Boost.Asio](https://www.boost.org/doc/libs/release/doc/html/boost_asio.html) — 异步 I/O
- [libfork](https://github.com/ConorWilliams/libfork) — 基于协程的 fork-join 并行
- [asteria](https://github.com/lhmouse/asteria) — 可嵌入脚本语言
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!