---
name: cpp-core-guidelines-style
source: https://app.decimal.ai/s/cpp-core-guidelines-style@1/SKILL.md
source_sha256: 23295088699e
---

# C++ Core Guidelines house style

## Contract

When you emit C++, name and shape every token the way the C++ Core Guidelines
(isocpp.github.io/CppCoreGuidelines) do: lowercase `snake_case` everywhere,
trailing-underscore private members, no `ALL_CAPS` outside macros, scoped
enums, brace initialization, `'\n'` for newlines, `using` aliases, guarded
headers. The defaults a general model reaches for (PascalCase types,
`MAX_SIZE` constants, `std::endl`) are the wrong house style here.

## Rules

This is the closed rule set. Apply every rule that the snippet touches.

1. **snake_case identifiers (NL.8/NL.10).** Namespaces, types (class /
   struct / enum / alias), free functions, member functions, variables, and
   parameters are all lowercase words joined by underscores. Never PascalCase,
   never camelCase. `class http_client`, `void send_request()`, `int
   retry_count`. A single-word name is simply lowercase: `socket`, not
   `Socket`.

2. **Trailing underscore on private data members.** Each non-static private
   or protected data member ends with exactly one trailing underscore:
   `buffer_`, `host_`, `byte_count_`. Do not use an `m_` prefix, a `_`
   prefix, or Hungarian tags. Public data members of an aggregate `struct`
   are snake_case **without** the trailing underscore.

3. **ALL_CAPS is for macros only (NL.9).** A named `const`/`constexpr`
   object is snake_case, never uppercase. Write `constexpr int max_retries`,
   not `MAX_RETRIES`. Reserve `ALL_CAPS` exclusively for `#define` macro
   names (which you should avoid anyway, ES.45/Enum.1).

4. **Enumerators are lowercase (Enum.5).** Enumerator names are snake_case
   lowercase: `red`, `not_found`, `in_progress`. Never `RED` / `NOT_FOUND`.

5. **Scoped `enum class`, never plain `enum` (Enum.3).** Declare every
   enumeration as `enum class name { ... };`. A bare unscoped `enum { ... }`
   leaks its names into the surrounding scope and is forbidden.

6. **`using` aliases, never `typedef` (T.43).** Type aliases use
   `using node_id = std::uint64_t;`. Never `typedef`.

7. **Brace-initialize at the point of declaration (ES.20/ES.23).** Every
   object is initialized where it is declared, and scalars / strings /
   aggregates use brace `{}` initialization: `int count{0};`, `std::string
   name{"x"};`. Never leave a variable uninitialized; prefer `{}` over `=`.

8. **`'\n'`, never `std::endl` (SL.io.50).** End a line of stream output
   with the character `'\n'`. `std::endl` forces a flush and must not appear.

9. **Guard every header (SF.8).** A header file begins with `#pragma once`
   **or** a matching `#ifndef X` / `#define X` pair closed by `#endif`.

10. **No `using namespace` at file scope in a header (SF.7).** Never place a
    `using namespace ...;` directive at global or namespace scope inside a
    header; it pollutes every translation unit that includes it.

## Worked examples (before -> after)

**Rule 1 — snake_case types and functions**
```cpp
// BEFORE (general-model default)
class HttpClient {
public:
    Response SendRequest(const std::string& url);
};

// AFTER (Core Guidelines house style)
class http_client {
public:
    response send_request(const std::string& url);
};
```

**Rule 2 — trailing underscore on private members**
```cpp
// BEFORE
class ring_buffer {
private:
    std::vector<int> data;
    std::size_t head;
    std::size_t m_tail;   // m_ prefix
};

// AFTER
class ring_buffer {
private:
    std::vector<int> data_;
    std::size_t head_;
    std::size_t tail_;
};
```

**Rule 3 — constants are not ALL_CAPS**
```cpp
// BEFORE
const int MAX_RETRIES = 3;
constexpr double TIMEOUT_SECONDS = 2.5;

// AFTER
constexpr int max_retries{3};
constexpr double timeout_seconds{2.5};
```

**Rules 4 & 5 — scoped enum, lowercase enumerators**
```cpp
// BEFORE
enum Color { RED, GREEN, BLUE };

// AFTER
enum class color { red, green, blue };
```

**Rule 6 — using, not typedef**
```cpp
// BEFORE
typedef std::vector<std::vector<int>> Matrix;

// AFTER
using matrix = std::vector<std::vector<int>>;
```

**Rule 7 — brace-initialize at declaration**
```cpp
// BEFORE
int port = 8080;
std::string host;          // uninitialized then assigned later

// AFTER
int port{8080};
std::string host{"localhost"};
```

**Rule 8 — '\n' over std::endl**
```cpp
// BEFORE
std::cout << "done" << std::endl;

// AFTER
std::cout << "done" << '\n';
```

**Rule 9 — guarded header**
```cpp
// BEFORE  (no guard)
#include <string>
class widget { /* ... */ };

// AFTER
#pragma once
#include <string>
class widget { /* ... */ };
```

**Rule 10 — no file-scope using namespace in a header**
```cpp
// BEFORE  (in a .h)
using namespace std;
class parser { string text_; };

// AFTER
class parser { std::string text_; };
```

## Edge cases & exceptions

- **Container element count vs initializer list.** Brace init means an
  initializer list: `std::vector<int> v{10};` is one element equal to 10. To
  create *ten* default elements, use parentheses: `std::vector<int> v(10);`.
  This parenthesis case is the one allowed exception to rule 7.
- **Public aggregate struct members** are snake_case **without** a trailing
  underscore — the underscore marks *private* state only (rule 2).
- **Out-of-line macros you cannot avoid** (include guards, `assert`) stay
  ALL_CAPS — that is exactly what rule 3 reserves caps for.
- **Template type parameters** may use a short PascalCase or single capital
  (`T`, `Iter`) per long-standing convention; rule 1 governs ordinary
  identifiers, not template parameters.
- An `#ifndef` guard and `#pragma once` are interchangeable for rule 9; pick
  one, do not require both.

## Do / Don't

- Do: `class tcp_session { int fd_; };` — Don't: `class TcpSession { int m_fd; };`
- Do: `constexpr int buffer_size{4096};` — Don't: `#define BUFFER_SIZE 4096`
- Do: `enum class state { idle, running };` — Don't: `enum State { IDLE, RUNNING };`
- Do: `using byte_span = std::span<std::byte>;` — Don't: `typedef ... ByteSpan;`
- Do: `std::cout << line << '\n';` — Don't: `std::cout << line << std::endl;`
- Do: `#pragma once` at top of header — Don't: unguarded header
- Do: `std::string s{name};` at declaration — Don't: `std::string s;` then assign

## Common mistakes

- Naming a class `PascalCase` out of C++ habit — the std library and these
  guidelines are snake_case; match them.
- Writing constants as `MAX_*` / `DEFAULT_*` in caps; caps belong to macros.
- Leaving enumerators in `ALL_CAPS` because "that's how enums look" — scoped
  enumerators are lowercase.
- Reaching for `std::endl` at the end of a print — use `'\n'` and flush
  explicitly only when you truly need to.
- Declaring a variable, then assigning on the next line. Initialize in place
  with braces.

## Quick checklist

- [ ] Every type / function / variable / namespace is lowercase snake_case
- [ ] Private data members end with one trailing underscore
- [ ] No ALL_CAPS except macro names
- [ ] Enumerations are `enum class`; enumerators lowercase
- [ ] Type aliases use `using`, not `typedef`
- [ ] Every object brace-initialized at its declaration
- [ ] Newlines are `'\n'`, never `std::endl`
- [ ] Headers carry a `#pragma once` or include guard
- [ ] No `using namespace` at file scope in a header
