C++ Core Guidelines Standards
Улучшите Claude Code с помощью C++ Core Guidelines. Обеспечьте соблюдение RAII, типобезопасности и современных стандартов C++17/20/23 для более безопасного, быстрого и поддерживаемого кода.
Этот навык наделяет Claude всесторонними знаниями C++ Core Guidelines, позволяя писать, рецензировать и рефакторить современный код на C++ (C++17/20/23) с акцентом на типобезопасность, управление ресурсами (RAII) и производительность. Он помогает разработчикам избегать типичных ошибок, таких как утечки памяти и неинициализированные переменные, применяя идиоматические шаблоны для функций, классов и обработки ошибок. Создаёте ли вы новую систему или модернизируете устаревшую кодовую базу — этот навык гарантирует, что ваш код на C++ будет надёжным, поддерживаемым и соответствовать лучшим отраслевым практикам.
Ключевые возможности
Сценарии использования
| name | cpp-coding-standards |
|---|---|
| description | C++ coding standards based on the C++ Core Guidelines (isocpp.github.io). Use when writing, reviewing, or refactoring C++ code to enforce modern, safe, and idiomatic practices. |
| origin | ECC |
Comprehensive coding standards for modern C++ (C++17/20/23) derived from the C++ Core Guidelines. Enforces type safety, resource safety, immutability, and clarity.
- Writing new C++ code (classes, functions, templates)
- Reviewing or refactoring existing C++ code
- Making architectural decisions in C++ projects
- Enforcing consistent style across a C++ codebase
- Choosing between language features (e.g.,
enumvsenum class, raw pointer vs smart pointer)
- Non-C++ projects
- Legacy C codebases that cannot adopt modern C++ features
- Embedded/bare-metal contexts where specific guidelines conflict with hardware constraints (adapt selectively)
These themes recur across the entire guidelines and form the foundation:
- RAII everywhere (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime
- Immutability by default (P.10, Con.1-5, ES.25): Start with
const/constexpr; mutability is the exception - Type safety (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time
- Express intent (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose
- Minimize complexity (F.2-3, ES.5, Per.4-5): Simple code is correct code
- Value semantics over pointer semantics (C.10, R.3-5, F.20, CP.31): Prefer returning by value and scoped objects
| Rule | Summary |
|---|---|
| P.1 | Express ideas directly in code |
| P.3 | Express intent |
| P.4 | Ideally, a program should be statically type safe |
| P.5 | Prefer compile-time checking to run-time checking |
| P.8 | Don't leak any resources |
| P.10 | Prefer immutable data to mutable data |
| I.1 | Make interfaces explicit |
| I.2 | Avoid non-const global variables |
| I.4 | Make interfaces precisely and strongly typed |
| I.11 | Never transfer ownership by a raw pointer or reference |
| I.23 | Keep the number of function arguments low |
// P.10 + I.4: Immutable, strongly typed interface
struct Temperature {
double kelvin;
};
Temperature boil(const Temperature& water);// Weak interface: unclear ownership, unclear units
double boil(double* temp);
// Non-const global variable
int g_counter = 0; // I.2 violation| Rule | Summary |
|---|---|
| F.1 | Package meaningful operations as carefully named functions |
| F.2 | A function should perform a single logical operation |
| F.3 | Keep functions short and simple |
| F.4 | If a function might be evaluated at compile time, declare it constexpr |
| F.6 | If your function must not throw, declare it noexcept |
| F.8 | Prefer pure functions |
| F.16 | For "in" parameters, pass cheaply-copied types by value and others by const& |
| F.20 | For "out" values, prefer return values to output parameters |
| F.21 | To return multiple "out" values, prefer returning a struct |
| F.43 | Never return a pointer or reference to a local object |
// F.16: Cheap types by value, others by const&
void print(int x); // cheap: by value
void analyze(const std::string& data); // expensive: by const&
void transform(std::string s); // sink: by value (will move)
// F.20 + F.21: Return values, not output parameters
struct ParseResult {
std::string token;
int position;
};
ParseResult parse(std::string_view input); // GOOD: return struct
// BAD: output parameters
void parse(std::string_view input,
std::string& token, int& pos); // avoid this// F.4 + F.8: Pure, constexpr where possible
constexpr int factorial(int n) noexcept {
return (n <= 1) ? 1 : n * factorial(n - 1);
}
static_assert(factorial(5) == 120);- Returning
T&&from functions (F.45) - Using
va_arg/ C-style variadics (F.55) - Capturing by reference in lambdas passed to other threads (F.53)
- Returning
const Twhich inhibits move semantics (F.49)
| Rule | Summary |
|---|---|
| C.2 | Use class if invariant exists; struct if data members vary independently |
| C.9 | Minimize exposure of members |
| C.20 | If you can avoid defining default operations, do (Rule of Zero) |
| C.21 | If you define or =delete any copy/move/destructor, handle them all (Rule of Five) |
| C.35 | Base class destructor: public virtual or protected non-virtual |
| C.41 | A constructor should create a fully initialized object |
| C.46 | Declare single-argument constructors explicit |
| C.67 | A polymorphic class should suppress public copy/move |
| C.128 | Virtual functions: specify exactly one of virtual, override, or final |
// C.20: Let the compiler generate special members
struct Employee {
std::string name;
std::string department;
int id;
// No destructor, copy/move constructors, or assignment operators needed
};// C.21: If you must manage a resource, define all five
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: Virtual destructor, use override
class Shape {
public:
virtual ~Shape() = default;
virtual double area() const = 0; // C.121: pure interface
};
class Circle : public Shape {
public:
explicit Circle(double r) : radius_(r) {}
double area() const override { return 3.14159 * radius_ * radius_; }
private:
double radius_;
};- Calling virtual functions in constructors/destructors (C.82)
- Using
memset/memcpyon non-trivial types (C.90) - Providing different default arguments for virtual function and overrider (C.140)
- Making data members
constor references, which suppresses move/copy (C.12)
| Rule | Summary |
|---|---|
| R.1 | Manage resources automatically using RAII |
| R.3 | A raw pointer (T*) is non-owning |
| R.5 | Prefer scoped objects; don't heap-allocate unnecessarily |
| R.10 | Avoid malloc()/free() |
| R.11 | Avoid calling new and delete explicitly |
| R.20 | Use unique_ptr or shared_ptr to represent ownership |
| R.21 | Prefer unique_ptr over shared_ptr unless sharing ownership |
| R.22 | Use make_shared() to make shared_ptrs |
// R.11 + R.20 + R.21: RAII with smart pointers
auto widget = std::make_unique<Widget>("config"); // unique ownership
auto cache = std::make_shared<Cache>(1024); // shared ownership
// R.3: Raw pointer = non-owning observer
void render(const Widget* w) { // does NOT own w
if (w) w->draw();
}
render(widget.get());// R.1: Resource acquisition is initialization
class FileHandle {
public:
explicit FileHandle(const std::string& path)
: handle_(std::fopen(path.c_str(), "r")) {
if (!handle_) throw std::runtime_error("Failed to open: " + 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_;
};- Naked
new/delete(R.11) malloc()/free()in C++ code (R.10)- Multiple resource allocations in a single expression (R.13 -- exception safety hazard)
shared_ptrwhereunique_ptrsuffices (R.21)
| Rule | Summary |
|---|---|
| ES.5 | Keep scopes small |
| ES.20 | Always initialize an object |
| ES.23 | Prefer {} initializer syntax |
| ES.25 | Declare objects const or constexpr unless modification is intended |
| ES.28 | Use lambdas for complex initialization of const variables |
| ES.45 | Avoid magic constants; use symbolic constants |
| ES.46 | Avoid narrowing/lossy arithmetic conversions |
| ES.47 | Use nullptr rather than 0 or NULL |
| ES.48 | Avoid casts |
| ES.50 | Don't cast away const |
// ES.20 + ES.23 + ES.25: Always initialize, prefer {}, default to const
const int max_retries{3};
const std::string name{"widget"};
const std::vector<int> primes{2, 3, 5, 7, 11};
// ES.28: Lambda for complex const initialization
const auto config = [&] {
Config c;
c.timeout = std::chrono::seconds{30};
c.retries = max_retries;
c.verbose = debug_mode;
return c;
}();- Uninitialized variables (ES.20)
- Using
0orNULLas pointer (ES.47 -- usenullptr) - C-style casts (ES.48 -- use
static_cast,const_cast, etc.) - Casting away
const(ES.50) - Magic numbers without named constants (ES.45)
- Mixing signed and unsigned arithmetic (ES.100)
- Reusing names in nested scopes (ES.12)
| Rule | Summary |
|---|---|
| E.1 | Develop an error-handling strategy early in a design |
| E.2 | Throw an exception to signal that a function can't perform its assigned task |
| E.6 | Use RAII to prevent leaks |
| E.12 | Use noexcept when throwing is impossible or unacceptable |
| E.14 | Use purpose-designed user-defined types as exceptions |
| E.15 | Throw by value, catch by reference |
| E.16 | Destructors, deallocation, and swap must never fail |
| E.17 | Don't try to catch every exception in every function |
// E.14 + E.15: Custom exception types, throw by value, catch by reference
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 to signal failure
throw NetworkError("connection refused", 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: Don't catch everything here -- let unexpected errors propagate
}- Throwing built-in types like
intor string literals (E.14) - Catching by value (slicing risk) (E.15)
- Empty catch blocks that silently swallow errors
- Using exceptions for flow control (E.3)
- Error handling based on global state like
errno(E.28)
| Rule | Summary |
|---|---|
| Con.1 | By default, make objects immutable |
| Con.2 | By default, make member functions const |
| Con.3 | By default, pass pointers and references to const |
| Con.4 | Use const for values that don't change after construction |
| Con.5 | Use constexpr for values computable at compile time |
// Con.1 through Con.5: Immutability by default
class Sensor {
public:
explicit Sensor(std::string id) : id_(std::move(id)) {}
// Con.2: const member functions by default
const std::string& id() const { return id_; }
double last_reading() const { return reading_; }
// Only non-const when mutation is required
void record(double value) { reading_ = value; }
private:
const std::string id_; // Con.4: never changes after construction
double reading_{0.0};
};
// Con.3: Pass by const reference
void display(const Sensor& s) {
std::cout << s.id() << ": " << s.last_reading() << '\n';
}
// Con.5: Compile-time constants
constexpr double PI = 3.14159265358979;
constexpr int MAX_SENSORS = 256;| Rule | Summary |
|---|---|
| CP.2 | Avoid data races |
| CP.3 | Minimize explicit sharing of writable data |
| CP.4 | Think in terms of tasks, rather than threads |
| CP.8 | Don't use volatile for synchronization |
| CP.20 | Use RAII, never plain lock()/unlock() |
| CP.21 | Use std::scoped_lock to acquire multiple mutexes |
| CP.22 | Never call unknown code while holding a lock |
| CP.42 | Don't wait without a condition |
| CP.44 | Remember to name your lock_guards and unique_locks |
| CP.100 | Don't use lock-free programming unless you absolutely have to |
// CP.20 + CP.44: RAII locks, always named
class ThreadSafeQueue {
public:
void push(int value) {
std::lock_guard<std::mutex> lock(mutex_); // CP.44: named!
queue_.push(value);
cv_.notify_one();
}
int pop() {
std::unique_lock<std::mutex> lock(mutex_);
// CP.42: Always wait with a condition
cv_.wait(lock, [this] { return !queue_.empty(); });
const int value = queue_.front();
queue_.pop();
return value;
}
private:
std::mutex mutex_; // CP.50: mutex with its data
std::condition_variable cv_;
std::queue<int> queue_;
};// CP.21: std::scoped_lock for multiple mutexes (deadlock-free)
void transfer(Account& from, Account& to, double amount) {
std::scoped_lock lock(from.mutex_, to.mutex_);
from.balance_ -= amount;
to.balance_ += amount;
}volatilefor synchronization (CP.8 -- it's for hardware I/O only)- Detaching threads (CP.26 -- lifetime management becomes nearly impossible)
- Unnamed lock guards:
std::lock_guard<std::mutex>(m);destroys immediately (CP.44) - Holding locks while calling callbacks (CP.22 -- deadlock risk)
- Lock-free programming without deep expertise (CP.100)
| Rule | Summary |
|---|---|
| T.1 | Use templates to raise the level of abstraction |
| T.2 | Use templates to express algorithms for many argument types |
| T.10 | Specify concepts for all template arguments |
| T.11 | Use standard concepts whenever possible |
| T.13 | Prefer shorthand notation for simple concepts |
| T.43 | Prefer using over typedef |
| T.120 | Use template metaprogramming only when you really need to |
| T.144 | Don't specialize function templates (overload instead) |
#include <concepts>
// T.10 + T.11: Constrain templates with standard concepts
template<std::integral T>
T gcd(T a, T b) {
while (b != 0) {
a = std::exchange(b, a % b);
}
return a;
}
// T.13: Shorthand concept syntax
void sort(std::ranges::random_access_range auto& range) {
std::ranges::sort(range);
}
// Custom concept for domain-specific constraints
template<typename T>
concept Serializable = requires(const T& t) {
{ t.serialize() } -> std::convertible_to<std::string>;
};
template<Serializable T>
void save(const T& obj, const std::string& path);- Unconstrained templates in visible namespaces (T.47)
- Specializing function templates instead of overloading (T.144)
- Template metaprogramming where
constexprsuffices (T.120) typedefinstead ofusing(T.43)
| Rule | Summary |
|---|---|
| SL.1 | Use libraries wherever possible |
| SL.2 | Prefer the standard library to other libraries |
| SL.con.1 | Prefer std::array or std::vector over C arrays |
| SL.con.2 | Prefer std::vector by default |
| SL.str.1 | Use std::string to own character sequences |
| SL.str.2 | Use std::string_view to refer to character sequences |
| SL.io.50 | Avoid endl (use '\n' -- endl forces a flush) |
// SL.con.1 + SL.con.2: Prefer vector/array over C arrays
const std::array<int, 4> fixed_data{1, 2, 3, 4};
std::vector<std::string> dynamic_data;
// SL.str.1 + SL.str.2: string owns, string_view observes
std::string build_greeting(std::string_view name) {
return "Hello, " + std::string(name) + "!";
}
// SL.io.50: Use '\n' not endl
std::cout << "result: " << value << '\n';| Rule | Summary |
|---|---|
| Enum.1 | Prefer enumerations over macros |
| Enum.3 | Prefer enum class over plain enum |
| Enum.5 | Don't use ALL_CAPS for enumerators |
| Enum.6 | Avoid unnamed enumerations |
// Enum.3 + Enum.5: Scoped enum, no ALL_CAPS
enum class Color { red, green, blue };
enum class LogLevel { debug, info, warning, error };
// BAD: plain enum leaks names, ALL_CAPS clashes with macros
enum { RED, GREEN, BLUE }; // Enum.3 + Enum.5 + Enum.6 violation
#define MAX_SIZE 100 // Enum.1 violation -- use constexpr| Rule | Summary |
|---|---|
| SF.1 | Use .cpp for code files and .h for interface files |
| SF.7 | Don't write using namespace at global scope in a header |
| SF.8 | Use #include guards for all .h files |
| SF.11 | Header files should be self-contained |
| NL.5 | Avoid encoding type information in names (no Hungarian notation) |
| NL.8 | Use a consistent naming style |
| NL.9 | Use ALL_CAPS for macro names only |
| NL.10 | Prefer underscore_style names |
// SF.8: Include guard (or #pragma once)
#ifndef PROJECT_MODULE_WIDGET_H
#define PROJECT_MODULE_WIDGET_H
// SF.11: Self-contained -- include everything this header needs
#include <string>
#include <vector>
namespace project::module {
class Widget {
public:
explicit Widget(std::string name);
const std::string& name() const;
private:
std::string name_;
};
} // namespace project::module
#endif // PROJECT_MODULE_WIDGET_H// NL.8 + NL.10: Consistent underscore_style
namespace my_project {
constexpr int max_buffer_size = 4096; // NL.9: not ALL_CAPS (it's not a macro)
class tcp_connection { // underscore_style class
public:
void send_message(std::string_view msg);
bool is_connected() const;
private:
std::string host_; // trailing underscore for members
int port_;
};
} // namespace my_projectusing namespace std;in a header at global scope (SF.7)- Headers that depend on inclusion order (SF.10, SF.11)
- Hungarian notation like
strName,iCount(NL.5) - ALL_CAPS for anything other than macros (NL.9)
| Rule | Summary |
|---|---|
| Per.1 | Don't optimize without reason |
| Per.2 | Don't optimize prematurely |
| Per.6 | Don't make claims about performance without measurements |
| Per.7 | Design to enable optimization |
| Per.10 | Rely on the static type system |
| Per.11 | Move computation from run time to compile time |
| Per.19 | Access memory predictably |
// Per.11: Compile-time computation where possible
constexpr auto lookup_table = [] {
std::array<int, 256> table{};
for (int i = 0; i < 256; ++i) {
table[i] = i * i;
}
return table;
}();
// Per.19: Prefer contiguous data for cache-friendliness
std::vector<Point> points; // GOOD: contiguous
std::vector<std::unique_ptr<Point>> indirect_points; // BAD: pointer chasing- Optimizing without profiling data (Per.1, Per.6)
- Choosing "clever" low-level code over clear abstractions (Per.4, Per.5)
- Ignoring data layout and cache behavior (Per.19)
Before marking C++ work complete:
- No raw
new/delete-- use smart pointers or RAII (R.11) - Objects initialized at declaration (ES.20)
- Variables are
const/constexprby default (Con.1, ES.25) - Member functions are
constwhere possible (Con.2) -
enum classinstead of plainenum(Enum.3) -
nullptrinstead of0/NULL(ES.47) - No narrowing conversions (ES.46)
- No C-style casts (ES.48)
- Single-argument constructors are
explicit(C.46) - Rule of Zero or Rule of Five applied (C.20, C.21)
- Base class destructors are public virtual or protected non-virtual (C.35)
- Templates are constrained with concepts (T.10)
- No
using namespacein headers at global scope (SF.7) - Headers have include guards and are self-contained (SF.8, SF.11)
- Locks use RAII (
scoped_lock/lock_guard) (CP.20) - Exceptions are custom types, thrown by value, caught by reference (E.14, E.15)
-
'\n'instead ofstd::endl(SL.io.50) - No magic numbers (ES.45)
Что это
Этот skill содержит адаптированную версию C++ Core Guidelines (с сайта isocpp.github.io) — свод правил и рекомендаций для написания современного кода на C++ (стандарты C++17/20/23). Правила разбиты по тематическим разделам: философия, функции, классы, управление ресурсами, обработка ошибок, шаблоны, конкурентность и т.д. Skill предназначен для использования в качестве эталона при написании, рецензировании или рефакторинге C++ кода, чтобы автоматизировать проверку соответствия лучшим практикам непосредственно в процессе работы (например, через интеграцию с редактором или в CI/CD).
Как работает
🧠 Философия и интерфейсы (P., I.)
Базовые принципы всего набора правил сформулированы в виде коротких постулатов, таких как "выражай намерение прямо в коде" (P.3), "предпочитай статическую типизацию" (P.4), "не утекай ресурсы" (P.8) и "отдавай предпочтение неизменяемым данным" (P.10). Правила из раздела I.* детализируют, как строить надёжные интерфейсы: I.1 — явные интерфейсы, I.4 — точная и строгая типизация, I.11 — никогда не передавать владение через сырой указатель или ссылку.
Пример правильного кода:
// P.10 + I.4: неизменяемый, строго типизированный интерфейс
struct Temperature {
double kelvin;
};
Temperature boil(const Temperature& water);
🔧 Функции (F.*)
Правила для функций охватывают всё: от сигнатуры и параметров до constexpr и noexcept. Ключевые рекомендации:
F.2: функция должна выполнять одно логическое действие.F.4: если может быть вычислена на этапе компиляции — делайconstexpr.F.6: если не бросает исключений — помечайnoexcept.F.16: для входных параметров: дешёвые типы — по значению, дорогие — по константной ссылке, а параметры-приёмники — по значению (с перемещением).F.20+F.21: выходные значения лучше возвращать, чем использовать выходные параметры.
Пример выбора способа передачи параметров:
// F.16: дешёвый тип — по значению
void print(int x);
// дорогой тип — по const&
void analyze(const std::string& data);
// приёмник владения — по значению (будет перемещён)
void transform(std::string s);
// F.20: возвращаем результат, а не заполняем out-параметры
ParseResult parse(std::string_view input); // хорошо
// void parse(std::string_view input, std::string& token, int& pos); // плохо
🧩 Классы и иерархии (C.*)
Основные правила касаются конструирования, копирования/перемещения и полиморфизма:
- Правило нуля (
C.20): если не нужно явно управлять ресурсами — не пиши специальные функции-члены. - Правило пяти (
C.21): если пришлось переопределить деструктор, копирование или перемещение — определи все пять. C.35: деструктор базового класса —public virtualилиprotected non-virtual.C.46: конструкторы с одним аргументом —explicit.C.128: для виртуальных функций указывай ровно один изvirtual,overrideилиfinal.
🛡️ Управление ресурсами (R.*)
Центральная идея — RAII (R.1). Ресурс привязывается к времени жизни объекта: открытие файла — в конструкторе, закрытие — в деструкторе. Сырые указатели (T*) трактуются как ненаблюдающие (R.3), владение передаётся только через умные указатели — std::unique_ptr и std::shared_ptr, создаваемые через std::make_unique/std::make_shared (R.20–R.22). new/delete (R.11) и malloc/free (R.10) — под запретом.
📝 Выражения и инструкции (ES.*)
Правила для повседневного кода:
- Инициализация обязательна (
ES.20). - Предпочитай
{}-инициализацию (ES.23). - По умолчанию —
constилиconstexpr(ES.25). - Используй
nullptrвместо0илиNULL(ES.47). - Избегай C-стиля приведения типов (
ES.48) и отбрасыванияconst(ES.50). - Магические числа заменяй именованными константами (
ES.45).
🚨 Обработка ошибок (E.*)
Стратегия с самого начала дизайна (E.1). Исключения — сигнал о том, что функция не может выполнить задачу (E.2). RAII гарантирует отсутствие утечек (E.6). Пользовательские типы исключений (E.14) бросай по значению, лови по ссылке (E.15). Деструкторы, deallocation и swap никогда не должны выбрасывать исключения (E.16). Не лови каждое исключение в каждой функции (E.17).
🔒 Константность и неизменяемость (Con.*)
Все правила в этом разделе призывают делать объекты иммутабельными по умолчанию (Con.1), функции-члены — константными (Con.2), а передаваемые по указателю/ссылке объекты — константными (Con.3). Значения, не изменяющиеся после конструирования, помечай как const (Con.4), а вычислимые на этапе компиляции — как constexpr (Con.5).
⚡ Конкурентность и параллелизм (CP.*)
Избегай гонок данных (CP.2), минимизируй разделяемое изменяемое состояние (CP.3), мысли в терминах задач, а не потоков (CP.4). Используй RAII для блокировок (std::scoped_lock, std::lock_guard), никогда не вызывай неизвестный код, удерживая блокировку (CP.22), и всегда ожидай с условием (std::condition_variable), а не без него (CP.42). volatile — не для синхронизации (CP.8), а lock-free программирование — только если нет другого выхода (CP.100).
📦 Шаблоны и обобщённое программирование (T.*)
Шаблоны — для повышения уровня абстракции (T.1) и выражения алгоритмов для разных типов (T.2). Обязательно указывай концепты для всех аргументов шаблонов (T.10, начиная с C++20). Используй стандартные концепты (std::integral, std::ranges::random_access_range), где возможно (T.11), и предпочитай краткий синтаксис (T.13). using вместо typedef (T.43). Специализация шаблонов функций — под запретом, используй перегрузку (T.144).
📚 Стандартная библиотека (SL.*)
Предпочитай std::vector и std::array C-массивам (SL.con.1, SL.con.2). Для строк — std::string (владение) и std::string_view (наблюдение) — SL.str.1, SL.str.2. Вместо std::endl используй '\n' — он не делает лишний flush (SL.io.50).
🏷️ Перечисления (Enum.*)
Только enum class вместо enum (Enum.3). Без ALL_CAPS для имён перечислителей (Enum.5), без безымянных перечислений (Enum.6). Перечисления предпочтительнее макросов #define (Enum.1).
📁 Файлы и именование (SF., NL.)
Код — в .cpp, интерфейсы — в .h (SF.1). В заголовках на глобальном уровне — ни using namespace (SF.7), используй #include-стражи (SF.8). Файлы .h должны быть самодостаточными (SF.11). В именовании — underscore_style (NL.10), ALL_CAPS — только для макросов (NL.9), никакого венгерского (NL.5).
🚀 Производительность (Per.*)
Не оптимизируй без причины (Per.1) и преждевременно (Per.2). Все утверждения о производительности подтверждай измерениями (Per.6). Переноси вычисления с этапа выполнения на этап компиляции (Per.11), предпочитай последовательные структуры данных для кеш-дружественности (Per.19).
Когда использовать
- Написание нового кода на C++: от классов и функций до шаблонов и многопоточных конструкций.
- Рецензирование и рефакторинг существующего кода для приведения его к современным стандартам.
- Архитектурные решения в C++ проектах: выбор между
enumиenum class, сырыми и умными указателями, передача параметров и т.д. - Обеспечение единого стиля во всей кодовой базе: от именования до структуры файлов.
- Checklist в конце skill (раздел "Quick Reference Checklist") можно использовать как готовый список для code review.
Важно знать
- Skill не предназначен для не-C++ проектов, для чистого C или для сред с жёсткими аппаратными ограничениями (bare-metal, embedded без поддержки C++17/20). В таких случаях правила нужно адаптировать выборочно.
- Это не дословная копия C++ Core Guidelines, а тематическая подборка с примерами и анти-паттернами, адаптированная для практического применения. Полный оригинал всегда доступен на сайте isocpp.github.io.
- Некоторые правила (например, про концепты
T.10,T.11) требуют C++20. Для более старых стандартов (C++17) их можно заменять наstatic_assertсtype_traitsили SFINAE, но это менее удобно. - В разделе "Concurrency & Parallelism" подразумевается использование стандартных примитивов синхронизации из
mutex,condition_variable,threadиfuture. Если проект использует сторонние библиотеки (например, TBB, Boost.Thread), аналоги правил могут отличаться.
Поддерживает ли этот навык старые версии C++?
Хотя навык ориентирован на современный C++ (17, 20 и 23), он предоставляет фундаментальные принципы безопасности, применимые к большинству версий, но рекомендует современные альтернативы устаревшим шаблонам.
Можно ли использовать этот навык для разработки на C++ во встраиваемых системах?
Да, но поскольку некоторые рекомендации могут конфликтовать с аппаратными ограничениями, рекомендуется адаптировать их выборочно для сред с прямым доступом к оборудованию.
Основан ли этот навык на каком-то отраслевом стандарте?
Да, он напрямую основан на официальных C++ Core Guidelines, поддерживаемых сообществом C++ на isocpp.github.io.
Как этот навык помогает управлять памятью?
Он строго применяет идиому RAII и использование умных указателей (unique_ptr, shared_ptr), не допуская применения обычных 'new', 'delete' и ручного выделения памяти в стиле C.
Синхронизируйте навыки с Claude Cowork, Claude Code, Codex и другими.
Установка одной командой.
npx skillfish add affaan-m/everything-claude-code cpp-coding-standardsИсточник: https://mcpmarket.com/tools/skills/c-core-guidelines-standards
Комментарии
Комментариев пока нет. Будьте первым.