c++-conventions/SKILL.md
C++ coding conventions and best practices for modern C++ development. Use when writing, reviewing, or refactoring C++ code to ensure consistency with project standards, const-correctness, RAII principles, and C++23 features.
npx skillsauth add heikopanjas/agent-skills c++-conventionsInstall 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.
std::unique_ptr, std::shared_ptr) over raw pointersconst when not modifiedconstconst references for complex types in parametersconst to return values when appropriatevoid SetTitle(const std::string& title);std::string GetTitle() const;const Data& GetData() const;void SetTitle(std::string title); (unnecessary copy)nullptr comparisons instead of implicit boolean conversion= is used instead of ==if (nullptr == ptr), if (0 == value), if (true == condition)if (!ptr), if (ptr == nullptr), if (value == 0)= is mistakenly used instead of ==std::unique_ptr for exclusive ownershipstd::shared_ptr for shared ownershipstd::weak_ptr to break circular dependencies// Good: RAII with smart pointers
auto data = std::make_unique<Data>();
auto shared = std::make_shared<SharedObject>();
// Good: RAII with containers
std::vector<int> numbers;
std::string text;
// Avoid: Raw pointers requiring manual cleanup
Data* data = new Data(); // Must remember to delete
Driver class → Driver.h and Driver.cpp)include/ directorysrc/ directory#pragma once at top#ifndef __MYPROJECT_CLASS_H_INCL__
#define __MYPROJECT_CLASS_H_INCL__
#include <string>
#include <vector>
#include "BaseClass.h"
// Forward declarations
class Helper;
class MyClass : public BaseClass
{
public:
// ... class definition
};
#endif // __MYPROJECT_CLASS_H_INCL__
#include "MyClass.h"
#include <algorithm>
#include <iostream>
#include "Helper.h"
namespace
{
// File-local helper functions
void LocalHelper()
{
// ...
}
}
// Class member implementations
MyClass::MyClass()
{
// ...
}
public, protected, privateclass MyClass
{
public:
// Constructors and destructor
MyClass();
virtual ~MyClass();
// Public interface
void PublicMethod();
int GetValue() const;
protected:
// Protected interface for derived classes
virtual void ProtectedMethod();
private:
// Private implementation details
void PrivateHelper();
int privateData_;
std::string privateName_;
};
Episode, MediaType)GetTitle, SetDuration, ParseInput)bufferSize, episodeCount)dataSize_, title_)MAX_EPISODE_LENGTH, DEFAULT_TIMEOUT)myproject, utils)T, ValueType)Model instead of P3Model)__PROJECT_CLASS_NAME_H_INCL__ where CLASS_NAME matches the classDriver → __MYPROJECT_DRIVER_H_INCL__TestTools → __MYPROJECT_TEST_TOOLS_H_INCL__#ifndef __MYPROJECT_DRIVER_H_INCL__
#define __MYPROJECT_DRIVER_H_INCL__
// Class declaration
#endif // __MYPROJECT_DRIVER_H_INCL__
#pragma pack// At top of header (after include guard, before includes)
#pragma pack(push, 8)
// ... class declarations ...
// At bottom of header (before closing include guard)
#pragma pack(pop)
using directives in headers (e.g., using namespace std;)using declarations sparingly in implementation filesnamespace myproject
{
namespace utils
{
class Helper { };
}
class MainClass { };
}
// C++17 nested namespace syntax
namespace myproject::utils
{
class Helper { };
}
auto)[[maybe_unused]] attribute or comment// Good: Clear parameter passing
void ProcessData(const std::vector<int>& data, int threshold);
// Good: Trailing return type with auto
auto GetValue() -> std::optional<int>;
// Good: Unused parameter handling
void Handler([[maybe_unused]] int eventType)
{
// Implementation doesn't use eventType
}
using instead of typedef for type aliases// Good: Clear type aliases
using UserId = uint64_t;
using ErrorCallback = std::function<void(const std::string&)>;
using DataMap = std::unordered_map<std::string, std::shared_ptr<Data>>;
// Avoid: Obscure typedef
typedef unsigned long long int ull;
enum class over enum for type safetyenum class)// Best: enum class (scoped and type-safe)
enum class Color : uint8_t
{
Red,
Green,
Blue
};
// Usage: Color::Red
// Acceptable: Traditional enum with prefix
enum MediaType
{
MEDIA_TYPE_AUDIO,
MEDIA_TYPE_VIDEO,
MEDIA_TYPE_SUBTITLE
};
std::optional for values that may not existstd::expected (C++23) or similar for expected errors// Good: Optional for nullable values
std::optional<User> FindUser(const std::string& name);
// Good: Exception for errors
void LoadFile(const std::string& path)
{
if (path.empty())
{
throw std::invalid_argument("Path cannot be empty");
}
// ... load file
}
// Good: Error handling with optional
auto user = FindUser("john");
if (user.has_value())
{
ProcessUser(user.value());
}
std::make_unique and std::make_shared for constructionnew and delete// Good: Smart pointers
auto data = std::make_unique<Data>();
auto shared = std::make_shared<Config>();
// Good: Stack allocation
Data localData;
std::array<int, 10> numbers;
// Good: Containers
std::vector<std::unique_ptr<Item>> items;
// for all comments (single-line and multi-line)/// for Doxygen comments\brief for brief descriptions\param for parameters\return for return values// comments for logic explanation/// \brief Sets the episode title
/// \param title The new title for the episode
void SetTitle(const std::string& title);
// Implementation comment explaining reasoning
// Use binary search because data is sorted
auto it = std::lower_bound(data.begin(), data.end(), target);
.clang-format configuration for automatic formatting// Function: opening brace on next line
void MyClass::ProcessData(const std::vector<int>& data)
{
// Control structure: opening brace on next line
if (nullptr == data_)
{
Initialize();
}
for (const auto& item : data)
{
ProcessItem(item);
}
}
auto for type deduction when type is obvious from contextstd::string_view for non-owning string referencesconstexpr for compile-time constants// Good: auto for obvious types
auto config = std::make_unique<Config>();
auto it = container.find(key);
// Good: Range-based for
for (const auto& item : items)
{
ProcessItem(item);
}
// Good: Structured bindings
auto [success, value] = TryParse(input);
// Good: string_view
void ProcessName(std::string_view name);
// Good: constexpr
constexpr int MAX_SIZE = 1024;
// C++20 concepts
template<typename T>
concept Drawable = requires(T obj)
{
obj.Draw();
};
template<Drawable T>
void Render(const T& object)
{
object.Draw();
}
// Traditional template with static_assert
template<typename T>
class Container
{
static_assert(std::is_default_constructible_v<T>,
"T must be default constructible");
};
[&] for local scope, by value [=] when neededmutable when lambda needs to modify captured values// Good: Short algorithm
std::sort(items.begin(), items.end(),
[](const Item& a, const Item& b)
{
return a.priority > b.priority;
});
// Good: Explicit captures
int threshold = 10;
auto filter = [threshold](int value)
{
return value > threshold;
};
// Good: Mutable lambda
int counter = 0;
auto increment = [counter]() mutable
{
return ++counter;
};
<algorithm> headervector, map, set, etc.)<string> for string handling<filesystem> (C++17) for file operations// Good: Standard algorithms
std::sort(data.begin(), data.end());
auto it = std::find_if(items.begin(), items.end(), predicate);
// Good: Standard containers
std::vector<int> numbers;
std::unordered_map<std::string, Data> cache;
// Good: Filesystem operations
std::filesystem::path filePath = "/path/to/file";
if (std::filesystem::exists(filePath))
{
// Process file
}
const for runtime constantsconstexpr for compile-time constantsconsteval (C++20) to force compile-time evaluationconstexpr when possible for compile-time optimization// Runtime constant
const int bufferSize = GetBufferSize();
// Compile-time constant
constexpr int MAX_USERS = 100;
// Constexpr function
constexpr int Square(int x)
{
return x * x;
}
// C++20 consteval (must be compile-time)
consteval int Factorial(int n)
{
return (n <= 1) ? 1 : n * Factorial(n - 1);
}
<cstdint>#ifdef _WIN32
// Windows-specific code
#include <windows.h>
#else
// POSIX code
#include <unistd.h>
#endif
// Use standard fixed-size types
uint32_t value32;
int64_t offset;
-Wall -Wextra -Wpedantic/W4TEST(MyClassTest, ConstructorInitializesCorrectly)
{
MyClass obj;
EXPECT_EQ(0, obj.GetValue());
}
TEST(MyClassTest, SetValueUpdatesCorrectly)
{
MyClass obj;
obj.SetValue(42);
EXPECT_EQ(42, obj.GetValue());
}
/// \brief Creates a new user account
/// \param username The unique username for the account
/// \param email The user's email address
/// \return A unique pointer to the created User object
/// \throws std::invalid_argument if username is empty
std::unique_ptr<User> CreateUser(
const std::string& username,
const std::string& email);
@dot...@enddot blocks for custom graphscmake_minimum_required(VERSION 3.20)
project(MyProject VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Library target
add_library(mylib
src/MyClass.cpp
src/Helper.cpp
)
target_include_directories(mylib
PUBLIC include
PRIVATE src
)
# Executable target
add_executable(myapp
src/main.cpp
)
target_link_libraries(myapp PRIVATE mylib)
testing
Record recent changes and decisions in the AGENTS.md file. Use when making project decisions, choosing technologies, establishing conventions, or completing significant changes that should be tracked in the project history.
development
Swift coding conventions and best practices for modern Swift development. Use when writing, reviewing, or refactoring Swift code to ensure consistency with naming conventions, access control, async/await patterns, and SwiftUI/framework best practices.
development
Determine whether a version bump is required after code changes and apply semantic versioning (SemVer). Use when making code changes, fixing bugs, adding features, or introducing breaking changes to a project that uses SemVer.
development
Rust coding conventions and best practices for idiomatic Rust development. Use when writing, reviewing, or refactoring Rust code to ensure consistency with error handling, RAII principles, naming conventions, and Rust edition best practices.