A coding standard is a written agreement about how your team writes C++. It covers things like how to name a class, where the opening brace goes, what header order looks like, and which dangerous patterns are off-limits. The three standards that dominate the industry today are Google's C++ Style Guide, LLVM's Coding Standards, and the C++ Core Guidelines from Bjarne Stroustrup and Herb Sutter, and each one was built for a different kind of team. This chapter walks through what each standard cares about, how to enforce them automatically with clang-format and clang-tidy, and how to pick one for your own project.
Open any C++ codebase older than a year and you will find at least three styles of writing the same thing. Someone wrote getProductPrice(). Someone else wrote get_product_price(). A third person wrote GetProductPrice. All three work. All three compile. But reading code, the inconsistency burns mental energy on every context switch between files.
A coding standard fixes this by making one set of choices once, for everyone. The benefits compound:
clang-format) and a linter (clang-tidy) can enforce it. The standard becomes machine-checkable.new/delete, C-style casts, uninitialized variables. Following the guide makes accidental bugs harder.The exact rules matter less than the fact that you have rules. Picking a slightly worse standard and following it consistently beats picking the perfect one and ignoring it.
Three standards have won the most adoption. They were built for different purposes, so comparing them is less about "which is best" and more about "which fits your team."
Google's guide was written for Google's monorepo, which is one of the largest C++ codebases on Earth. The guide is opinionated, prescriptive, and unapologetically pragmatic. If a feature is too easy to misuse at Google's scale, the guide bans it outright. Exceptions are banned. Implicit conversions are restricted. Header files must use #define guards in a very specific format.
It is strict, but the strictness is the point. Google has tens of thousands of engineers touching the same code, so the guide optimizes for "any engineer can read any file." It is a good fit for large teams, monorepos, and projects where consistency matters more than personal preference.
LLVM is the compiler infrastructure project that produces Clang. Its standard is less restrictive than Google's, but more opinionated than the C++ Core Guidelines. LLVM cares about compiler-writing patterns: clean class hierarchies, RTTI alternatives like dyn_cast, careful memory management. The standard's naming convention is unusual, with CamelCase even for variables, which trips up newcomers.
LLVM's guide is a great fit for systems projects, library code, and anything where you need to interoperate with the LLVM ecosystem.
The C++ Core Guidelines, maintained by Stroustrup and Sutter, are different in kind. They are not a style guide. They do not dictate naming or brace placement. Instead, they are a set of rules about how to write safer, faster, more modern C++. Rules like "prefer std::array to C arrays" or "do not return raw pointers that own memory" or "use RAII to manage all resources."
The guidelines come with the Guidelines Support Library (GSL), a small header-only library that provides types like gsl::span, gsl::not_null, and gsl::owner to make the rules expressible in code. They are meant to be combined with a style guide, not replace one. A common setup is "use Google's style for formatting and naming, and the Core Guidelines for everything else."
The diagram shows how the three standards relate. Google and LLVM are full style guides covering formatting, naming, and language usage. The Core Guidelines are different. They focus on safe and modern usage patterns and rely on the GSL library for runtime support.
Naming is where the three standards differ most visibly. The same code written three ways:
Google style. Variables and members in lower_snake_case. Types in PascalCase. Member variables get a trailing underscore. Functions are PascalCase for free functions, but accessor methods that read like fields stay lowercase.
LLVM style. Variables, members, and functions all use camelCase starting with a lowercase letter. Types stay PascalCase. No trailing underscores on members.
Core Guidelines (loose). The Core Guidelines do not mandate a specific naming style, but the examples in the document use snake_case for everything except types. This matches the standard library, which is what most "modern C++" code looks like.
The full naming convention comparison:
| Entity | LLVM | Core Guidelines (typical) | |
|---|---|---|---|
| Class / Struct | PascalCase | PascalCase | snake_case or PascalCase |
| Function | PascalCase | camelCase | snake_case |
| Variable | snake_case | camelCase | snake_case |
| Member variable | snake_case_ (trailing _) | camelCase | snake_case_ |
| Constant | kPascalCase | PascalCase | snake_case or ALL_CAPS |
| Macro | ALL_CAPS | ALL_CAPS | ALL_CAPS |
| Enum value | kPascalCase | PascalCase | snake_case |
| File | lower_snake_case.cc | PascalCase.cpp | flexible |
A few observations. Macros are ALL_CAPS everywhere, which is the one near-universal rule in C++. The Google k-prefix for constants (like kMaxCartSize = 100) is unusual and many teams skip it even when otherwise following Google's style. LLVM's all-camelCase approach is unusual at first but reads cleanly with practice.
Formatting is where humans waste the most time and where automation pays off the most. The specific decisions each standard makes are below.
Google uses 2-space indentation, opening brace on the same line as the declaration (Egyptian or K&R style), and short single-statement blocks may omit braces, though most teams add them back via a clang-tidy rule.
LLVM also uses 2-space indentation but places access specifiers (public:, private:) at the class's indent level rather than offset.
The Core Guidelines have no opinion on indentation or brace placement. Stroustrup himself prefers what is called "Stroustrup style" (else on its own line after a closing brace), but the guidelines do not mandate it.
Google caps lines at 80 columns. LLVM also caps at 80. Many modern teams have relaxed this to 100 or 120 because monitors got wider, but 80 remains the default in both guides. That is why C++ code often looks narrower than code in other languages.
This one matters because the order of includes can affect what compiles. Google specifies a strict order:
cart.cc, include cart.h first).<stdio.h>).<vector>, <string>).Each group is separated by a blank line, and includes within a group are alphabetized.
Why include your own header first? It catches the bug where cart.h is missing an #include it actually needs. If you include the standard library first, the missing include gets satisfied without warning, and the bug only shows up when someone else includes cart.h in isolation.
LLVM uses a similar order but flips the first two groups. The C++ Core Guidelines say nothing about order, only that headers should be self-contained.
Header order is not a runtime cost, but it directly impacts compile time. Including heavy standard headers like <algorithm> or <regex> in your own headers forces every translation unit that pulls you in to also compile those headers. Keep .h files minimal and push heavy includes into .cc files.
A style guide is only as good as your enforcement. clang-format is the standard tool. It reads a file, applies your style rules, and writes back the reformatted code. Run it on save, in CI, or as a pre-commit hook.
Configure it with a file named .clang-format at the root of your project. A working configuration for a Google-style project:
You can also start from BasedOnStyle: LLVM or BasedOnStyle: Mozilla or BasedOnStyle: WebKit and override individual fields. The BasedOnStyle field handles most of your decisions, and you override only the few things you care about.
To run it:
Most editors integrate with clang-format directly. VS Code, CLion, Vim, and Emacs all have plugins that reformat on save. Once this is set up, you stop thinking about formatting.
clang-format only handles layout. It will not catch a missing override keyword or a raw new that should be a std::make_unique. That is where clang-tidy comes in. It is a static analyzer that runs hundreds of checks against your code, ranging from style nitpicks to bug detection.
Configuration lives in a .clang-tidy file. A reasonable starting point:
What this config does, in plain terms:
-*), then enables five families: bugprone- for real bug patterns, cppcoreguidelines- to enforce Core Guidelines rules, modernize- to flag C++98 patterns that should be updated, performance- for common performance smells, and readability- for code clarity.bugprone- warnings as errors so they break the build.readability-identifier-naming: classes are CamelCase, variables are lower_case, private members end with _. This is the part that checks names against your standard.To run it:
The -- separates clang-tidy arguments from compiler arguments. clang-tidy needs to know how the file would be compiled to do its analysis, which is why CMake's compile_commands.json is useful: it lists every file's exact compile invocation.
clang-tidy is slow. A full repo run can take 5-10x longer than a regular build because each translation unit goes through the full Clang front end plus check passes. Most teams run it on changed files only in pre-commit hooks and do a full sweep in nightly CI.
The diagram below shows how the two tools fit into a typical workflow.
The pipeline has three enforcement points. The editor reformats on save so you never have to think about it. The pre-commit hook re-runs the formatter and a fast subset of clang-tidy checks on changed files only. CI runs the full check set against the whole codebase. Each layer catches issues the earlier layer missed.
A function that violates several Core Guidelines:
What is wrong with this code?
clang-tidy with cppcoreguidelines-* and modernize-* enabled flags several issues:
cppcoreguidelines-prefer-member-initializer).name is taken by value and copied into the parameter, then copied again into the member. It should be moved (modernize-pass-by-value or use a member init list with std::move).print() does not modify state and should be const (readability-make-member-function-const).name and price are public, breaking encapsulation. This is not auto-fixed but several checks flag it indirectly.new and delete are flagged in favor of std::make_unique (modernize-make-unique, cppcoreguidelines-owning-memory).Fix:
The fixed version uses an initializer list, moves the string parameter, marks Print() as const, makes members private, and uses std::make_unique so memory is released automatically when product goes out of scope. Topics like const correctness and smart pointers get full chapters later in this section.
The C++ Core Guidelines reference a few types that do not exist in the standard library. They live in the Guidelines Support Library, a small header-only library available from Microsoft's gsl-lite or the original Microsoft/GSL repos. The three most common types:
A non-owning view over a contiguous range of objects. Think "pointer plus length, but type-safe." Use it in function signatures instead of T* and a separate size parameter.
With gsl::span, the function is written once and called with total(cart) or total(deals) directly. No need to pass .data() and .size() separately, and no risk of mismatching them. C++20 added std::span, which is the standard library version of the same idea, so new code should prefer that.
A wrapper around a pointer that promises it is never null. The promise is checked at construction. After that, the type signals to readers (and to clang-tidy) that no null check is needed.
If you pass nullptr to a not_null parameter, you get a contract violation. The type makes the assumption explicit instead of leaving it as a comment that nobody reads.
A marker on a raw pointer that says "this pointer owns the memory it points to." It does not change runtime behavior, but tools like clang-tidy use it to verify that exactly one delete exists for each owner.
In modern C++, gsl::owner is rare because std::unique_ptr does the same job at runtime. But when maintaining old code that cannot yet move to smart pointers, marking owning pointers with gsl::owner lets static analysis check ownership correctness.
A side-by-side summary of what each standard cares about:
| Concern | LLVM | Core Guidelines | |
|---|---|---|---|
| Scope | Full style + language usage | Full style + language usage | Language usage only |
| Naming | PascalCase types/functions, snake_case_ members | PascalCase types, camelCase functions/variables | Loose, examples use snake_case |
| Indentation | 2 spaces | 2 spaces | Unspecified |
| Line length | 80 | 80 | Unspecified |
| Brace style | Attached (K&R) | Attached (K&R) | Unspecified |
| Exceptions | Banned in Google code | Generally avoided | Allowed and encouraged |
RTTI / dynamic_cast | Discouraged | Banned (uses llvm::dyn_cast) | Allowed |
Raw new/delete | Avoid; use smart pointers | Avoid; use smart pointers | Avoid; use smart pointers or RAII |
| Macros | Avoid except for include guards | Avoid where possible | Strongly discouraged |
| Companion library | Abseil | LLVM Support | GSL |
| Best for | Large monorepos, big teams | Compiler/library projects | Any project, paired with a style guide |
A few takeaways from the table. Google and LLVM are full prescriptive style guides. They tell you the brace style, the line length, the indent width. The Core Guidelines are a higher-level safety and modern-usage rule set, and they expect to be paired with a separate style guide. Most teams that "follow the Core Guidelines" actually follow the Core Guidelines plus a formatter config.
The Google ban on exceptions is famous and controversial. Inside Google, the codebase predates standardized exception handling and the cost of converting was deemed too high. New projects outside Google rarely have that constraint, so most teams enable exceptions even when otherwise following Google's style.
There is no universal best choice. The right standard depends on your team and your project. A few questions that point you in the right direction.
How big is the team? Solo or 2-3 engineers? The Core Guidelines plus your own .clang-format will work. 50+ engineers? Pick Google or LLVM. The prescriptive nature pays off when nobody knows everyone else's preferences.
What is the codebase context? Compiler or LLVM tooling? Use LLVM's standards. Working on a Google open-source library? Use Google's. Greenfield application code? Start with Google or LLVM as a base and layer the Core Guidelines on top.
Are you using exceptions? If yes, Google's guide will fight you. If no, Google is a clean fit.
Do you have an existing codebase? Match what is already there. Switching styles on a 100k-line codebase is rarely worth the diff churn. A .clang-format that matches the current style and a .clang-tidy that enforces a few safety rules is a far better investment.
Do you care more about safety or consistency? The Core Guidelines win on safety. Google and LLVM win on consistency. You do not have to pick one. The common setup is Google or LLVM for layout and naming, Core Guidelines (via clang-tidy) for the safety rules.
A practical starter recipe for a new project:
BasedOnStyle: Google (or LLVM) in .clang-format.cppcoreguidelines-*, modernize-*, bugprone-*, and performance-* check families in .clang-tidy.Migrating a large codebase to a new style is a one-time cost that scales with file count. A repo with 10k files and many active feature branches will hit merge-conflict pain during the migration. Run the formatter in one massive PR, freeze branches for an hour, and use git blame --ignore-revs-file so the formatting commit does not pollute blame history.
10 quizzes