| name | cpp-patterns-advanced |
| description | Advanced C++ standards — concurrency (CP.*), templates & C++20 concepts (T.*), standard library (SL.*), enumerations (Enum.*), source files & naming (SF.*/NL.*), performance (Per.*), and a full quick-reference checklist. Extends cpp-coding-standards. |
C++ Coding Standards — Advanced
This skill extends cpp-coding-standards with concurrency, templates/concepts, standard library, enumerations, naming, performance, and the full checklist.
Concurrency & Parallelism (CP.*)
Key Rules
| Rule | Summary |
|---|
| CP.1 | Assume code will run as part of a multi-threaded program |
| CP.2 | Avoid data races |
| CP.20 | Use RAII — never lock()/unlock() directly |
| CP.21 | Use std::lock() to acquire multiple mutexes |
| CP.22 | Never call unknown code while holding a lock |
| CP.31 | Pass small amounts of data between threads by value |
| CP.32 | Don't share owning raw pointers between threads |
| CP.42 | Don't spuriously wait |
| CP.44 | Remember to name lock_guards and unique_locks |
RAII Locking
class BankAccount {
public:
void deposit(double amount) {
std::lock_guard<std::mutex> lock{mutex_};
balance_ += amount;
}
bool withdraw(double amount) {
std::lock_guard<std::mutex> lock{mutex_};
if (balance_ < amount) return false;
balance_ -= amount;
return true;
}
double balance() const {
std::lock_guard<std::mutex> lock{mutex_};
return balance_;
}
private:
mutable std::mutex mutex_;
double balance_{0.0};
};
Multi-Mutex Lock (deadlock prevention)
void transfer(BankAccount& from, BankAccount& to, double amount) {
std::unique_lock<std::mutex> lock1{from.mutex_, std::defer_lock};
std::unique_lock<std::mutex> lock2{to.mutex_, std::defer_lock};
std::lock(lock1, lock2);
from.balance_ -= amount;
to.balance_ += amount;
}
Async and Futures
std::future<int> compute_async(std::vector<int> data) {
return std::async(std::launch::async, [data = std::move(data)] {
return std::accumulate(data.begin(), data.end(), 0);
});
}
void run_parallel() {
auto f1 = std::async(std::launch::async, task1);
auto f2 = std::async(std::launch::async, task2);
auto r1 = f1.get();
auto r2 = f2.get();
return r1 + r2;
}
Anti-Patterns
lock()/unlock() directly — use lock_guard (CP.20)
- Holding a lock while calling callbacks or virtual functions (CP.22)
shared_ptr for thread data — prefer value passing (CP.31/CP.32)
- Unnamed
lock_guard — dies immediately, provides no protection (CP.44)
Templates & Generic Programming (T.*)
Key Rules
| Rule | Summary |
|---|
| T.1 | Use templates to raise abstraction, not just for type parameterization |
| T.10 | Specify concepts for all template arguments |
| T.11 | Use standard concepts whenever practical |
| T.12 | Prefer concept names over auto in variable declarations |
| T.20 | Avoid "concepts" without meaningful semantics |
| T.41 | Require only essential properties in concepts |
| T.60 | Minimize template context dependence |
C++20 Concepts
#include <concepts>
template<std::integral T>
T safe_add(T a, T b) {
if (b > 0 && a > std::numeric_limits<T>::max() - b)
throw std::overflow_error("overflow");
return a + b;
}
template<typename T>
concept Printable = requires(T t, std::ostream& os) {
{ os << t } -> std::same_as<std::ostream&>;
};
template<Printable T>
void print_all(const std::vector<T>& items) {
for (const auto& item : items)
std::cout << item << '\n';
}
template<typename T>
concept Sortable = std::ranges::range<T>
&& std::totally_ordered<std::ranges::range_value_t<T>>;
template<Sortable Container>
void sort(Container& c) {
std::ranges::sort(c);
}
SFINAE vs Concepts (before/after C++20)
template<typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
T old_style(T x) { return x * 2; }
template<std::integral T>
T new_style(T x) { return x * 2; }
template<typename T>
requires std::integral<T>
T also_new_style(T x) { return x * 2; }
Variadic Templates
template<typename... Args>
void log(std::string_view fmt, Args&&... args) {
std::cout << std::vformat(fmt, std::make_format_args(args...)) << '\n';
}
template<typename... Ts>
auto sum(Ts... values) {
return (values + ...);
}
Anti-Patterns
- Unconstrained
template<typename T> where a concept applies (T.10)
- Using templates for simple runtime polymorphism (prefer virtual functions)
- Deep template nesting — extract named concepts or helper templates
Standard Library (SL.*)
Key Rules
| Rule | Summary |
|---|
| SL.1 | Use the standard library where possible |
| SL.con.1 | Prefer vector by default; use array for fixed size |
| SL.con.2 | Prefer vector over deque unless you need front insertion |
| SL.str.1 | Use std::string for owned strings |
| SL.str.2 | Use std::string_view for non-owning string references |
bool starts_with(std::string_view s, std::string_view prefix) {
return s.substr(0, prefix.size()) == prefix;
}
std::vector<int> scores;
scores.reserve(100);
std::array<double, 3> point{1.0, 2.0, 3.0};
auto it = std::find_if(scores.begin(), scores.end(),
[](int s) { return s > 90; });
auto high_scores = scores | std::views::filter([](int s) { return s > 90; })
| std::views::take(10);
Enumerations (Enum.*)
All Rules
| Rule | Summary |
|---|
| Enum.1 | Prefer enumerations over macros |
| Enum.2 | Use enumeration to represent sets of named constants |
| Enum.3 | Prefer enum class over plain enum |
| Enum.4 | Define operations on enumerations for safe use |
| Enum.5 | Don't use ALL_CAPS for enumerators |
| Enum.6 | Avoid unnamed enumerations |
| Enum.7 | Specify the underlying type only if necessary |
| Enum.8 | Specify enumerator values only if necessary |
enum class Color { Red, Green, Blue };
Color operator+(Color c, int n) {
return static_cast<Color>(static_cast<int>(c) + n);
}
std::ostream& operator<<(std::ostream& os, Color c) {
switch (c) {
case Color::Red: return os << "Red";
case Color::Green: return os << "Green";
case Color::Blue: return os << "Blue";
}
return os << "unknown";
}
enum class Status : uint8_t { Active = 1, Inactive = 2, Deleted = 3 };
enum Direction { North, South };
int North = 5;
Source Files & Naming (SF., NL.)
Naming Conventions
| Element | Convention | Example |
|---|
| Classes/Structs | UpperCamelCase | BankAccount |
| Functions | lower_snake_case | get_balance() |
| Variables | lower_snake_case | account_id |
Constants/constexpr | ALL_CAPS or k prefix | MAX_SIZE, kMaxRetries |
| Template parameters | UpperCamelCase | template<typename ValueType> |
| Private members | trailing_underscore_ | balance_ |
| Macros | ALL_CAPS (avoid macros) | ASSERT_TRUE |
Source File Rules
| Rule | Summary |
|---|
| SF.1 | Use .cpp for code files, .h or .hpp for interfaces |
| SF.2 | Header files must not contain object definitions or non-inline function definitions |
| SF.3 | Use #pragma once or include guards |
| SF.4 | Include standard library headers before project headers |
| SF.7 | Don't write using namespace in headers |
| SF.8 | Use #include guards for all header files |
#pragma once
#include <string>
#include <vector>
#include "myproject/core.h"
namespace myproject {
class Widget {
public:
explicit Widget(std::string name);
std::string name() const;
private:
std::string name_;
};
}
Performance Guidelines (Per.*)
Key Rules
| Rule | Summary |
|---|
| Per.1 | Don't optimize without reason — measure first |
| Per.4 | Don't assume that complicated code is necessarily faster |
| Per.5 | Don't assume that low-level code is necessarily faster |
| 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.14 | Minimize the number of allocations and deallocations |
| Per.15 | Do not allocate on a critical branch |
| Per.19 | Access memory predictably (cache locality) |
constexpr size_t fibonacci(size_t n) {
return (n <= 1) ? n : fibonacci(n-1) + fibonacci(n-2);
}
constexpr auto fib10 = fibonacci(10);
std::vector<int> results;
results.reserve(expected_count);
for (int col = 0; col < N; ++col)
for (int row = 0; row < N; ++row)
sum += matrix[row][col];
for (int row = 0; row < N; ++row)
for (int col = 0; col < N; ++col)
sum += matrix[row][col];
void process(const std::vector<int>& data) {
for (int x : data) use(x);
}
Quick Reference Checklist
Philosophy & Interfaces
Functions
Classes
Resource Management
Expressions & Statements
Error Handling
Constants & Immutability
Concurrency
Templates
Enumerations
Performance