skills/catalog/cpp/cpp-development/SKILL.md
Use when writing, reviewing, or refactoring C++ code — covers workflow, essential patterns, and pre-commit checklist for modern C++17/20.
npx skillsauth add erikstmartin/dotfiles cpp-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
-Wall -Wextra -Wpedantic), and C++ standard versionconstexpr, smart pointers, and zero-overhead abstractions-fsanitize=address) and UBSan (-fsanitize=undefined); run static analysisperf, valgrind --tool=callgrind); measure before and after any changeLet the compiler generate all special members. Compose from types that already manage their own resources.
// GOOD: Rule of Zero — compiler generates correct copy/move/destructor
struct Employee {
std::string name;
std::string department;
int id{0};
// No destructor, no copy/move needed
};
If you manage a raw resource directly, define (or =delete) all five special members.
// Rule of Five: owning a raw resource via unique_ptr handles it cleanly
class Buffer {
public:
explicit Buffer(std::size_t n)
: data_{std::make_unique<char[]>(n)}, size_{n} {}
~Buffer() = default;
Buffer(Buffer&&) noexcept = default;
Buffer& operator=(Buffer&&) noexcept = default;
Buffer(const Buffer& o)
: data_{std::make_unique<char[]>(o.size_)}, size_{o.size_} {
std::copy_n(o.data_.get(), size_, data_.get());
}
Buffer& operator=(const Buffer& o) {
if (this != &o) {
auto nd = std::make_unique<char[]>(o.size_);
std::copy_n(o.data_.get(), o.size_, nd.get());
data_ = std::move(nd);
size_ = o.size_;
}
return *this;
}
private:
std::unique_ptr<char[]> data_;
std::size_t size_;
};
void print(int x); // cheap (≤ pointer size): by value
void analyze(const std::string& data); // expensive, read-only: const&
void consume(std::string s); // sink (will move/store): by value
auto widget = std::make_unique<Widget>("cfg"); // default: unique ownership
auto cache = std::make_shared<Cache>(1024); // shared ownership only when needed
void render(const Widget* w); // raw ptr = non-owning observer only
class Sensor {
public:
explicit Sensor(std::string id) : id_{std::move(id)} {}
const std::string& id() const { return id_; } // const member fn
double reading() const { return val_; }
void record(double v) { val_ = v; } // non-const only when needed
private:
const std::string id_; // never changes after construction
double val_{0.0};
};
// RAII mutex lock — always named, never plain lock()/unlock()
void update(Sensor& s, double v) {
std::lock_guard<std::mutex> lock{mtx_}; // released at scope exit
s.record(v);
}
#include <concepts>
template<std::integral T>
T gcd(T a, T b) {
while (b) a = std::exchange(b, a % b);
return a;
}
// Custom concept
template<typename T>
concept Serializable = requires(const T& t) {
{ t.serialize() } -> std::convertible_to<std::string>;
};
template<Serializable T>
void save(const T& obj, std::string_view path);
enum class Color { red, green, blue }; // scoped, no name pollution
enum class LogLevel { debug, info, warning, error };
// BAD: enum { RED, GREEN }; — leaks names, clashes with macros
new/delete — use smart pointers or RAII wrappersconst/constexpr by default; mutate only when neededenum class instead of plain enumnullptr instead of 0 or NULLexplicitstatic_assert#pragma once or #ifndef include guards and are self-containedlock_guard/scoped_lock), never lock()/unlock()const&constexpr constantsCalling virtual functions in constructors/destructors — the derived override is not active yet; behavior is undefined or wrong silently.
memset/memcpy on non-trivial types — bypasses constructors/destructors; use assignment or std::copy instead.
Unnamed lock_guard: std::lock_guard<std::mutex>(m); — this is a temporary that destructs immediately, leaving the mutex unlocked. Always name it: std::lock_guard<std::mutex> lk{m};.
volatile for thread synchronization — volatile prevents compiler reordering but provides no CPU memory-order guarantees. Use std::atomic<T> or proper mutexes.
Catching exceptions by value — catches a sliced copy. Always catch (const MyException& e), never catch (MyException e).
testing
Use when creating new skills, editing existing skills, or verifying skills work before deployment
development
Use when you have a spec or requirements for a multi-step task, before touching code
data-ai
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
tools
Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions