Communitygithub.com

C++ 编码规范 (C++ Core Guidelines)

基于 C++ Core Guidelines (isocpp.github.io) 的 C++ 编码规范。在编写、审查或重构 C++ 代码时使用,以强制实施现代、安全且惯用的实践。

C++ 编码规范 (C++ Core Guidelines) 是什么?

该技能提供了一套基于 C++ Core Guidelines 的综合编码规范,专注于现代 C++(C++17/20/23)的最佳实践。它的主要目标是通过强制实施类型安全、资源安全、不变性和代码清晰度,帮助开发者编写更安全、更可靠且更易于维护的 C++ 代码。该技能规定了何时使用(例如,编写新代码、审查现有代码、做架构决策)以及何时不应使用(例如,非 C++ 项目、遗留 C 代码库)。其核心原则包括全面使用 RAII、默认使用不可变性、类型安全、表达意图、最小化复杂性以及优先使用值语义而非指针语义。该文档详细涵盖了哲学与接口、函数、类与类层次结构、资源管理、表达式与语句、错误处理、常量与不变性以及并发与并行性等主题,每个主题都包含关键规则的表格、正确的代码示例('DO')、错误的代码示例('DON'T')以及需要避免的反模式。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/cpp-coding-standards

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

C++ 编码规范 (C++ Core Guidelines) 是做什么的?

基于 C++ Core Guidelines 的现代 C++ (C++17/20/23) 综合编码规范。强制实施类型安全、资源安全、不变性和清晰度。

何时使用

  • 编写新的 C++ 代码(类、函数、模板)
  • 审查或重构现有的 C++ 代码
  • 在 C++ 项目中做出架构决策
  • 在 C++ 代码库中强制执行一致的风格
  • 在语言特性之间做出选择(例如,enum 与 enum class,原始指针与智能指针)

何时不应使用

  • 非 C++ 项目
  • 无法采用现代 C++ 功能的遗留 C 代码库
  • 特定规则与硬件约束冲突的嵌入式/裸机环境(请有选择地适配)

横切原则

以下主题贯穿整个指南,构成其基础:

  1. 处处使用 RAII (P.8, R.1, E.6, CP.20):将资源生命周期与对象生命周期绑定
  2. 默认使用不可变性 (P.10, Con.1-5, ES.25):从 const/constexpr 开始;可变性是例外
  3. 类型安全 (P.4, I.4, ES.46-49, Enum.3):使用类型系统在编译时防止错误
  4. 表达意图 (P.3, F.1, NL.1-2, T.10):名称、类型和概念应传达目的
  5. 最小化复杂性 (F.2-3, ES.5, Per.4-5):简单的代码是正确的代码
  6. 值语义优先于指针语义 (C.10, R.3-5, F.20, CP.31):优先按值返回和使用作用域对象

哲学与接口 (P., I.)

关键规则

规则摘要
P.1直接在代码中表达想法
P.3表达意图
P.4理想情况下,程序应该是静态类型安全的
P.5优先选择编译时检查而非运行时检查
P.8不要泄漏任何资源
P.10优先选择不可变数据而非可变数据
I.1使接口显式化
I.2避免非 const 全局变量
I.4使接口精确且具有强类型
I.11永远不要通过原始指针或引用传递所有权
I.23保持函数参数数量较少

正确做法 (DO)

// P.10 + I.4: 不可变、强类型接口
struct Temperature {
    double kelvin;
};

Temperature boil(const Temperature& water);

错误做法 (DON'T)

// 弱接口:所有权不明确,单位不明确
double boil(double* temp);

// 非 const 全局变量
int g_counter = 0;  // I.2 违规

函数 (F.*)

关键规则

规则摘要
F.1将有意义的操作打包为精心命名的函数
F.2一个函数应执行单个逻辑操作
F.3保持函数简短简单
F.4如果函数可能在编译时求值,则将其声明为 constexpr
F.6如果函数必须不抛出异常,则将其声明为 noexcept
F.8优先使用纯函数
F.16对于“输入”参数,将廉价复制的类型按值传递,其他类型按 const& 传递
F.20对于“输出”值,优先使用返回值而非输出参数
F.21要返回多个“输出”值,优先返回结构体
F.43永远不要返回指向局部对象的指针或引用

参数传递

// F.16: 廉价类型按值传递,其他类型按 const&
void print(int x);                           // 廉价:按值传递
void analyze(const std::string& data);       // 昂贵:按 const& 传递
void transform(std::string s);               // 接收型:按值传递(将会移动)

// F.20 + F.21: 返回值,而非输出参数
struct ParseResult {
    std::string token;
    int position;
};

ParseResult parse(std::string_view input);   // 好:返回结构体

// 坏:输出参数
void parse(std::string_view input,
           std::string& token, int& pos);    // 避免这样

纯函数和 constexpr

// F.4 + F.8: 尽可能使用纯函数 constexpr
constexpr int factorial(int n) noexcept {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

static_assert(factorial(5) == 120);

反模式

  • 从函数返回 T&& (F.45)
  • 使用 va_arg / C 风格可变参数 (F.55)
  • 在传递给其他线程的 lambda 中按引用捕获 (F.53)
  • 返回 const T 抑制移动语义 (F.49)

类与类层次结构 (C.*)

关键规则

规则摘要
C.2如果存在不变式,使用 class;如果数据成员独立变化,使用 struct
C.9最小化成员的暴露
C.20如果可以避免定义默认操作,则这样做(零规则)
C.21如果定义或 =delete 任何拷贝/移动/析构函数,则处理它们全部(五规则)
C.35基类析构函数:应为公开虚函数或受保护的非虚函数
C.41构造函数应创建一个完全初始化的对象
C.46声明单参数构造函数为 explicit
C.67多态类应抑制公开的拷贝/移动
C.128虚函数:应精确指定 virtual、override 或 final 之一

零规则

// C.20: 让编译器生成特殊成员
struct Employee {
    std::string name;
    std::string department;
    int id;
    // 不需要析构函数、拷贝/移动构造函数或赋值操作符
};

五规则

// C.21: 如果必须管理资源,则定义全部五个
class Buffer {
public:
    explicit Buffer(std::size_t size)
        : data_(std::make_unique<char[]>(size)), size_(size) {}

    ~Buffer() = default;

    Buffer(const Buffer& other)
        : data_(std::make_unique<char[]>(other.size_)), size_(other.size_) {
        std::copy_n(other.data_.get(), size_, data_.get());
    }

    Buffer& operator=(const Buffer& other) {
        if (this != &other) {
            auto new_data = std::make_unique<char[]>(other.size_);
            std::copy_n(other.data_.get(), other.size_, new_data.get());
            data_ = std::move(new_data);
            size_ = other.size_;
        }
        return *this;
    }

    Buffer(Buffer&&) noexcept = default;
    Buffer& operator=(Buffer&&) noexcept = default;

private:
    std::unique_ptr<char[]> data_;
    std::size_t size_;
};

类层次结构

// C.35 + C.128: 虚析构函数,使用 override
class Shape {
public:
    virtual ~Shape() = default;
    virtual double area() const = 0;  // C.121: 纯接口
};

class Circle : public Shape {
public:
    explicit Circle(double r) : radius_(r) {}
    double area() const override { return 3.14159 * radius_ * radius_; }

private:
    double radius_;
};

反模式

  • 在构造函数/析构函数中调用虚函数 (C.82)
  • 在非平凡类型上使用 memset/memcpy (C.90)
  • 为虚函数及其覆盖器提供不同的默认参数 (C.140)
  • 将数据成员设为 const 或引用,这会抑制移动/拷贝 (C.12)

资源管理 (R.*)

关键规则

规则摘要
R.1使用 RAII 自动管理资源
R.3原始指针 (T*) 是非拥有的
R.5优先使用作用域对象;不要不必要地分配堆内存
R.10避免使用 malloc()/free()
R.11避免显式调用 new 和 delete
R.20使用 unique_ptr 或 shared_ptr 来表示所有权
R.21除非需要共享所有权,否则优先使用 unique_ptr 而非 shared_ptr
R.22使用 make_shared() 来创建 shared_ptr

智能指针使用

// R.11 + R.20 + R.21: 使用 RAII 和智能指针
auto widget = std::make_unique<Widget>("config");  // 独占所有权
auto cache  = std::make_shared<Cache>(1024);        // 共享所有权

// R.3: 原始指针 = 非拥有观察者
void render(const Widget* w) {  // 不拥有 w
    if (w) w->draw();
}

render(widget.get());

RAII 模式

// R.1: 资源获取即初始化
class FileHandle {
public:
    explicit FileHandle(const std::string& path)
        : handle_(std::fopen(path.c_str(), "r")) {
        if (!handle_) throw std::runtime_error("无法打开: " + path);
    }

    ~FileHandle() {
        if (handle_) std::fclose(handle_);
    }

    FileHandle(const FileHandle&) = delete;
    FileHandle& operator=(const FileHandle&) = delete;
    FileHandle(FileHandle&& other) noexcept
        : handle_(std::exchange(other.handle_, nullptr)) {}
    FileHandle& operator=(FileHandle&& other) noexcept {
        if (this != &other) {
            if (handle_) std::fclose(handle_);
            handle_ = std::exchange(other.handle_, nullptr);
        }
        return *this;
    }

private:
    std::FILE* handle_;
};

反模式

  • 裸 new/delete (R.11)
  • 在 C++ 代码中使用 malloc()/free() (R.10)
  • 在单个表达式中进行多次资源分配 (R.13 -- 异常安全隐患)
  • 在 unique_ptr 足够的情况下使用 shared_ptr (R.21)

表达式与语句 (ES.*)

关键规则

规则摘要
ES.5保持作用域较小
ES.20始终初始化对象
ES.23优先使用 {} 初始化语法
ES.25除非有意修改,否则将对象声明为 const 或 constexpr
ES.28对 const 变量的复杂初始化使用 lambda
ES.45避免魔法常量;使用符号常量
ES.46避免缩窄/有损算术转换
ES.47使用 nullptr 而非 0 或 NULL
ES.48避免使用强制转换
ES.50不要去掉 const 限定

初始化

// ES.20 + ES.23 + ES.25: 始终初始化,优先使用 {},默认使用 const
const int max_retries{3};
const std::string name{"widget"};
const std::vector<int> primes{2, 3, 5, 7, 11};

// ES.28: 对复杂的 const 初始化使用 lambda
const auto config = [&] {
    Config c;
    c.timeout = std::chrono::seconds{30};
    c.retries = max_retries;
    c.verbose = debug_mode;
    return c;
}();

反模式

  • 未初始化的变量 (ES.20)
  • 使用 0 或 NULL 作为指针 (ES.47 -- 使用 nullptr)
  • C 风格强制转换 (ES.48 -- 使用 static_cast、const_cast 等)
  • 去掉 const 限定 (ES.50)
  • 没有命名常量的魔法数字 (ES.45)
  • 混合有符号和无符号算术 (ES.100)
  • 在嵌套作用域中重用名称 (ES.12)

错误处理 (E.*)

关键规则

规则摘要
E.1在设计早期制定错误处理策略
E.2抛出异常以指示函数无法执行其分配的任务
E.6使用 RAII 防止泄漏
E.12当不可能或不接受抛出异常时,使用 noexcept
E.14使用专门设计的用户定义类型作为异常
E.15按值抛出,按引用捕获
E.16析构函数、内存释放和 swap 绝不能失败
E.17不要试图在每一个函数中捕获每一个异常

异常层次结构

// E.14 + E.15: 自定义异常类型,按值抛出,按引用捕获
class AppError : public std::runtime_error {
public:
    using std::runtime_error::runtime_error;
};

class NetworkError : public AppError {
public:
    NetworkError(const std::string& msg, int code)
        : AppError(msg), status_code(code) {}
    int status_code;
};

void fetch_data(const std::string& url) {
    // E.2: 抛出异常以指示失败
    throw NetworkError("连接被拒绝", 503);
}

void run() {
    try {
        fetch_data("https://api.example.com");
    } catch (const NetworkError& e) {
        log_error(e.what(), e.status_code);
    } catch (const AppError& e) {
        log_error(e.what());
    }
    // E.17: 不要在这里捕获所有异常 -- 让意外的异常传播出去
}

反模式

  • 抛出来自内置类型如 int 或字符串字面量 (E.14)
  • 按值捕获(存在切片风险)(E.15)
  • 静默吞没错误的空 catch 块
  • 使用异常进行流程控制 (E.3)
  • 基于全局状态如 errno 的错误处理 (E.28)

常量与不变性 (Con.*)

所有规则

规则摘要
Con.1默认情况下,使对象不可变
Con.2默认情况下,使成员函数为 const
Con.3默认情况下,将指针和引用传递给 const
Con.4对于构造后不会变化的值,使用 const
Con.5对于可在编译时计算的值,使用 constexpr
// Con.1 到 Con.5: 默认使用不可变性
class Sensor {
public:
    explicit Sensor(std::string id) : id_(std::move(id)) {}

    // Con.2: 默认情况下,成员函数为 const
    const std::string& id() const { return id_; }
    double last_reading() const { return reading_; }

    // 仅当需要修改时才为非 const
    void record(double value) { reading_ = value; }

private:
    const std::string id_;  // Con.4: 构造后永不变化
    double reading_{0.0};
};

// Con.3: 按 const 引用传递
void display(const Sensor& s) {
    std::cout << s.id() << ": " << s.last_reading() << '\n';
}

// Con.5: 编译时常量
constexpr double PI = 3.14159265358979;
constexpr int MAX_SENSORS = 256;

并发与并行性 (CP.*)

关键规则

规则摘要
CP.2避免数据竞争
CP.3最小化可写数据的显式共享
CP.4从任务的角度思考,而不是线程
CP.8不要使用 volatile 进行同步
CP.20使用 RAII,永远不要使用裸 lock()/unlock()
CP.21使用 std::scoped_lock 获取多个互斥量
CP.22在持有锁时,绝不要调用未知代码
CP.42不要无条件等待
CP.44记住命名你的 lock_guard 和 unique_lock
CP.100除非绝对必要,否则不要使用无锁编程

安全锁定

// CP.20 + CP.44: RAII 锁,始终命名
class ThreadSafeQueue {
public:
    void push(int value) {
        std::scoped_lock lock(mutex_);  // CP.21: 同时锁定所有内容
        queue_.push(value);
    }

    std::optional<int> pop() {
        std::scoped_lock lock(mutex_);
        if (queue_.empty()) return std::nullopt;
        auto val = queue_.front();
        queue_.pop();
        return val;
    }

private:
    std::queue<int> queue_;
    mutable std::mutex mutex_;
};

反模式

  • 持有锁时调用未知代码 (CP.22)
  • 无条件等待 (std::condition_variable 无谓词) (CP.42)
  • 将 volatile 用于线程同步 (CP.8)
  • 裸 lock()/unlock() 而不是 RAII (CP.20)
  • 对简单任务使用共享数据而不是消息传递

模板与泛型编程 (T.*)

关键规则

规则摘要
T.1使用模板来提升代码的抽象层次
T.2在泛型代码中使用概念来指定要求
T.10为所有模板参数指定概念
T.20避免使用“概念”而不使用 requires 来模拟概念
T.41仅在模板中需要时,才在类模板中定义好友函数
T.47避免带有未命名、未 lambda 化的参数的模板
T.65当转发参数是完美转发时,使用 auto&&

概念

// T.10: 指定概念以使意图清晰并改进错误消息
template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

template<Hashable T>
void process(const T& value) {
    auto hash = std::hash<T>{}(value);
    // ...
}

完美转发

// T.65: 使用 auto&& 进行完美转发
template<typename... Args>
auto make_widget(Args&&... args) {
    return std::make_unique<Widget>(std::forward<Args>(args)...);
}

反模式

  • 对简单代码使用模板 (T.5)
  • 模板使用的类型上非必要的限制
  • 不指定概念(在 C++20 中)(T.10)

性能 (Per.*)

关键规则

规则摘要
Per.1不要无理由地优化
Per.2不要过早优化
Per.3不要优化尚未配置的内容
Per.4不要假设复杂代码必然比简单代码快
Per.5先编写清晰的代码,然后再优化
Per.7设计以支持优化
Per.10依赖静态类型系统

要点

  • 首先关注清晰的架构和正确的设计
  • 使用 RAII 避免动态内存分配
  • 当需要避免堆分配时,优先选择 std::array 而非 std::vector
  • 将成员按照对齐要求从最大到最小排序,以最小化填充
  • 通过 const/constexpr 为优化器提供尽可能多的信息
// Per.5 + Per.7: 首先是清晰的代码,性能优化紧随其后
// 清晰的循环比晦涩的优化更容易让编译器优化

反模式

  • 假设没有配置的代码中存在的瓶颈 (Per.3)
  • 使用低效算法而不是更好的数据结构 (Per.13 -- 使用 O(n log n) 排序,而非 O(n^2) 冒泡排序)
  • 在 std::array 足够时使用 std::vector 处理小尺寸

源文件与命名空间 (SF., SL.)

关键规则

规则摘要
SF.1使用 .cpp 后缀表示实现文件,.h 表示接口文件
SF.2头文件不得包含对象定义或非内联函数定义
SF.3在头文件中使用 namespace 来限定全局作用域
SF.4头文件必须是自包含的
SF.5.cpp 文件必须包含定义其接口的 .h 文件
SF.6使用 using namespace 指令进行转换,但对于库代码,在 .cpp 文件中使用它们
SF.7不要在头文件作用域中使用 using namespace
SF.8为所有代码使用 #include 防护
SF.21不要使用未命名的命名空间在头文件中拥有内部链接

包含顺序

// SF.5: cpp 文件包含自己的头文件优先
#include "widget.h"       // 相关联的头文件

#include <vector>          // 标准库
#include <boost/format.hpp> // 第三方库

#include "utils.h"        // 项目内部的头文件

反模式

  • 在头文件中使用 using namespace std; (SF.7)
  • 包含顺序违反本地优先规则
  • 缺少 #pragma once 或包含防护 (SF.8)
  • 在头文件中定义非内联、非模板的函数 (SF.2)

补充资源

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

相关技能