The guide + private style inspector

Write C++
people trust.

Learn the standard, then check your own code against it. Get line-level guidance for readable, typed, testable, production-ready C++—without sending your code anywhere.

Evidence-led, not dogmatic.
Reviewed against primary sources · July 2026

service.cpp ● typed
#include <algorithm>
#include <ranges>
#include <string>
#include <vector>

std::vector<std::string> active_names(const std::vector<User>& users) {
    std::vector<std::string> names;
    for (const auto& user : users) {
        if (user.is_active()) {
            names.push_back(user.name());
        }
    }
    std::ranges::sort(names);
    return names;
}
All checks passed 0.18s
Aa Readable by defaultOptimize for the next person.
Low noiseTools handle the trivia.

Built-in utility

Paste. Inspect.
Improve.

Get an immediate, line-by-line review based on this guide’s core principles. Every finding explains what matters and suggests a concrete next move.

Private by designYour code never leaves this browser.
Your C++
0 lines · 0 characters
+ Enter

FORMATIndentation · whitespace · line length · statements

INTENTNaming · RAII · type contracts · complexity

SAFETYExceptions · secrets · shell use · timeouts

SCOPEFast heuristic review · not a parser or CI replacement

01Instant style reviewLine-level, private feedback

02Core Guidelines groundedCanonical rules, with context

03Production mindedRAII, tests, security, CI

04Actionable guidanceEvery finding explains why

The north star

Style is how code
communicates intent.

Great C++ feels intentional. Readers spend their attention on the problem—not decoding clever syntax, inconsistent structure, or invisible side effects.

01

Readability counts

Choose explicit names, linear control flow, and small interfaces. Code is read far more often than it is written.

Make intent visible
02

Boundaries matter

Validate at the edges, type public contracts, and keep side effects easy to find. Make invalid states hard to represent.

Design clear contracts
03

Automate consistency

Let formatters, linters, type checkers, and tests settle mechanical questions before review begins.

Build the safety net

Express ideas directly in code.
Represent ownership explicitly.
Make invalid states hard to express.

— C++ Core Guidelines, ISO C++ guidance

The practical guide

From first line to
production.

Use these as strong defaults. Every rule has a purpose; when the rule obscures that purpose, use judgment and document the exception.

01

Format & name

Remove friction
before it starts.

Formatting should be boring, predictable, and automated. Names should carry enough meaning that comments explain why, not what a variable contains.

01.1

Indent with four spaces

Never mix tabs and spaces. Use hanging indents for multiline expressions, and align closing delimiters with the construct that opened them.

auto report = build_report(
    source,
    include_metadata,
);
01.2

Use a deliberate line length

Many C++ teams use 100 or 120 characters, with clang-format enforcing the choice. Pick one limit, configure it once, and let the formatter enforce it.

100common120large-codebase default
01.3

Name for the reader

Use one consistent naming convention: many C++ teams choose snake_case for functions and variables, PascalCase for types, and kPascalCase or UPPER_SNAKE_CASE for constants. Avoid vague containers like data, info, and utils.

invoice_totalvariable PaymentClientclass kMaxRetriesconstant
× Avoid
Hidden meaning
double calc(std::vector<Item> items, bool f) {
    double x = 0;
    for (auto item : items) x += item.value;
    return f ? x * 1.2 : x;
}
Prefer
Intent in the names
Money invoice_total(std::span<const LineItem> line_items,
                    IncludeTax include_tax) {
    Money subtotal{};
    for (const auto& item : line_items) {
        subtotal += item.price();
    }
    return include_tax == IncludeTax::yes
        ? subtotal * kTaxRate
        : subtotal;
}
02

Modern C++ patterns

Idiomatic,
not clever.

Modern C++ uses the language’s strengths without turning every abstraction into a puzzle. Prefer clear ownership, simple control flow, and named algorithms over clever templates.

Guard early

Return or throw at the boundary so the happy path stays flat and readable.

Use truth naturally

Prefer if (!items.empty()) to size comparisons; use nullptr only for pointer absence.

Compare correctly

Use == for value semantics, named predicates for domain meaning, and avoid assignment inside conditions.

Iterate directly

Reach for range-for, standard algorithms, spans, and views before manually managing indices.

orders.cpp
Receipt dispatch(const Order& order, Warehouse& warehouse) {
    if (order.items().empty()) {
        throw EmptyOrderError{order.id()};
    }

    if (order.status() != OrderStatus::paid) {
        throw OrderNotPaidError{order.id()};
    }

    std::vector<Item> available;
    for (const auto& item : order.items()) {
        if (warehouse.has_stock(item.sku())) {
            available.push_back(item);
        }
    }
    return warehouse.dispatch(available);
}
1 Guard clauses expose invalid states. 2 An enum class makes a closed state explicit. 3 The loop names the filtering step.
03

Types & boundaries

Turn assumptions
into contracts.

Concrete types, spans, string views, optionals, and concepts make interfaces searchable and mistakes cheaper. They are most valuable at public APIs, domain boundaries, and code that changes often.

Wide in

Accept the broadest interface you need: std::span, std::string_view, iterators, ranges, or concepts.

Narrow out

Return a concrete, predictable type. Use std::optional or std::expected when absence or failure is part of the contract.

Any with intent

Use templates and concepts when the abstraction is real; avoid type erasure until runtime polymorphism is needed.

pricing.cpp
#include <span>
#include <vector>

struct Priced {
    Money price;
};

struct BasketSummary {
    std::size_t item_count{};
    Money total{};
};

BasketSummary summarize(std::span<const Priced> items) {
    Money total{};
    for (const auto& item : items) {
        total += item.price;
    }
    return {.item_count = items.size(), .total = total};
}
i

Set a version floor. Declare CMAKE_CXX_STANDARD or target_compile_features, then use the modern syntax that floor supports. For current code, prefer standard vocabulary types such as std::vector<std::string> and absence types such as std::optional<std::string>.

04

Functions & data

Keep the center
of gravity small.

Functions

  • Do one thing at one level of abstraction.
  • Make dependencies and side effects explicit.
  • Use strong types or enum classes for booleans and ambiguous values.
  • Return consistently; use std::optional, std::expected, or exceptions deliberately.

Data & classes

  • Prefer plain data structures until behavior needs a home.
  • Use simple structs for value-oriented records.
  • Prefer composition over inheritance.
  • Expose small public APIs; keep internals replaceable.
05

Errors & logging

Fail with context,
not confusion.

Errors are part of your API. Catch only what you can handle, preserve context, and make the operational trail useful without leaking sensitive data.

× Avoid
Swallowed failure
try {
    charge(card);
} catch (...) {
    std::cout << "Something went wrong\n";
    return false;
}
Prefer
Precise and traceable
try {
    auto receipt = gateway.charge(request);
} catch (const GatewayTimeout& error) {
    logger.warn("Payment timed out", order.id());
    throw PaymentUnavailable{order.id(), error};
}
05.1

Use domain exceptions

Name the failure in the language of the caller. Keep exception hierarchies shallow.

05.2

Libraries log, applications configure

Libraries should report errors clearly and avoid global logging configuration. Applications own sinks, levels, and formatting.

05.3

Never log secrets

Treat tokens, credentials, payment data, and personal information as toxic. Redact at the boundary.

06

Project structure

Make the right place
obvious.

acme-engine/

├── CMakeLists.txt
├── include/
│   └── acme/
│       ├── domain.hpp
│       └── service.hpp
├── src/
│   ├── domain.cpp
│   └── service.cpp
└── tests/
    └── service_test.cpp
01

Use CMakeLists.txt

Centralize build metadata and supported tool configuration.

02

Consider a src layout

It separates public headers, implementation files, and tests so dependencies stay visible.

03

Organize by responsibility

Prefer cohesive modules over a catch-all utils.hpp. Mirror concepts, not framework jargon.

Include order

1Standard library2Third party3Project headers
One include per line; group standard, third-party, and project headers; avoid catch-all headers in public interfaces.
07

Testing

Test behavior,
not choreography.

FastUnit tests run constantly.

FocusedOne behavior, clear failure.

FaithfulIntegration tests cover real boundaries.

IndependentNo order or shared-state surprises.

test_pricing.cpp
#include <catch2/catch_test_macros.hpp>

TEST_CASE("discounted_total applies percentage discounts") {
    CHECK(discounted_total(Money{100}, 0.10) == Money{90});
    CHECK(discounted_total(Money{0}, 0.10) == Money{0});
}

Coverage is a map, not a target. Use it to find untested risk; a high percentage cannot prove useful assertions.

08

Reliability

Be safe under
real conditions.

Security

  • Never hard-code secrets.
  • Validate untrusted input at boundaries.
  • Avoid eval, unsafe deserialization, and shell strings.
  • Audit dependencies and lock applications reproducibly.

Performance

  • Measure before optimizing.
  • Fix algorithms and I/O before micro-tuning syntax.
  • Benchmark representative workloads.
  • Keep performance choices readable and documented.

Concurrency

  • Use std::jthread, task systems, or event loops with explicit cancellation.
  • Use threads deliberately for blocking I/O.
  • Use thread pools, SIMD, or processes for CPU work after measuring.
  • Bound queues, fan-out, lifetimes, and shutdown paths.
!

Make failure finite. Network calls need explicit timeouts, retries need backoff and a cap, queues need bounds, and destructors/shutdown paths need tests.

The modern toolchain

Automate the
boring parts.

Use one command locally and the same command in CI. The exact tools can change; the feedback loop should remain fast and dependable.

A pragmatic baseline

One file.
Shared expectations.

Put supported configuration in CMakeLists.txt plus checked-in clang-format and clang-tidy files. Start focused; add stricter rules because they catch problems your team actually has—not because a tool offers them.

  • Formatting and include order are automatic.
  • Rule selection is explicit and reviewable.
  • The C++ standard is declared once.
CMakeLists.txt
cmake_minimum_required(VERSION 3.24)
project(your_project VERSION 0.1.0 LANGUAGES CXX)

add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)
target_compile_options(project_options INTERFACE
    $<$<CXX_COMPILER_ID:Clang,GNU>:-Wall -Wextra -Wpedantic -Wconversion>
    $<$<CXX_COMPILER_ID:MSVC>:/W4 /permissive->
)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE project_options)

enable_testing()
# Add Catch2 or GoogleTest targets here.

NoteThis is a starting point, not universal law. Match the C++ standard, clang-tidy checks, sanitizer policy, and dependency model to the oldest platform you actually support.

Before you merge

The ten-minute
quality pass.

A compact review for the risks automation cannot fully understand. Your progress is saved on this device.

0/ 10 complete

The C++ bookshelf

Six books worth
keeping close.

Category-defining bestsellers and enduring practitioner favorites, selected for a useful path from first program to maintainable production systems.

Start herePrimerTour

Write better codeEffectiveTemplates

Build for changeLarge-scalePatterns

Cover of C++ Primer
World bestseller
Beginner2012

C++ Primer

Lippman · Lajoie · Moo · 5th edition

A comprehensive path through C++ fundamentals, library types, classes, templates, and idioms.

Best forA structured first serious C++ book
View at publisher
Cover of A Tour of C++
Practical favorite
Fast overview2022

A Tour of C++

Bjarne Stroustrup · 3rd edition

A compact tour of modern C++ language and library features from the language’s creator.

Best forExperienced programmers getting current quickly
View at publisher
Cover of Effective Modern C++
Best-practice desk guide
Intermediate2014

Effective Modern C++

Scott Meyers

Concise, specific guidance on type deduction, smart pointers, move semantics, lambdas, and concurrency.

Best forTurning working C++ into excellent C++
View at publisher
Cover of C++ Templates, 2nd Edition
Template classic
Intermediate–advanced2022

C++ Templates

Vandevoorde · Josuttis · Gregor · 2nd edition

A deep guide to templates, specialization, overload resolution, metaprogramming, and generic libraries.

Best forUnderstanding generic C++ deeply
View at publisher
Cover of Large-Scale C++
Team-scale favorite
Team-scale2019

Large-Scale C++

John Lakos

Process, architecture, physical design, dependencies, and scaling practices for long-lived C++ systems.

Best forMaking large codebases survivable
View at publisher
Cover of Design Patterns
Systems design pick
Design1994

Design Patterns

Gamma · Helm · Johnson · Vlissides

The classic pattern vocabulary for object-oriented design, still useful when applied with modern C++ restraint.

Best forShared architecture vocabulary
View at publisher

Editorial noteSelections are independent and use current English-language editions reviewed July 2026. Labels are editorial recommendations, not live sales rankings. No affiliate links or paid placements.

Primary sources

Go deeper.

This guide synthesizes standards and practice. When precision matters, follow the living source.

C++ is a standardized programming language maintained through ISO. This independent guide is not affiliated with or endorsed by ISO, WG21, or the Standard C++ Foundation. Last editorial review: July 19, 2026.

Copied to clipboard