| name | cpp-expert |
| version | 1.0.0 |
| description | Expert-level C++ development with modern C++20/23, STL, memory management, and performance |
| category | languages |
| tags | ["cpp","c++","c++20","c++23","stl","templates","performance","memory"] |
| allowed-tools | ["Read","Write","Edit","Bash(g++:*, clang++:*, cmake:*, make:*)"] |
C++ Expert
Expert guidance for modern C++ development including C++20/23 features, STL, templates, memory management, and high-performance programming.
Core Concepts
Modern C++ Features (C++20/23)
- Concepts and constraints
- Ranges and views
- Coroutines
- Modules
- Three-way comparison (spaceship operator)
- std::format
- std::span
- Designated initializers
- consteval and constinit
Memory Management
- RAII (Resource Acquisition Is Initialization)
- Smart pointers (unique_ptr, shared_ptr, weak_ptr)
- Move semantics and perfect forwarding
- Memory allocation strategies
- Custom allocators
- Memory pools
Performance
- Zero-cost abstractions
- Inline optimization
- Template metaprogramming
- Compile-time computation (constexpr)
- Cache-friendly data structures
- SIMD operations
Modern C++ Syntax
Concepts (C++20)
#include <concepts>
#include <iostream>
#include <vector>
template<typename T>
concept Numeric = std::integral<T> || std::floating_point<T>;
template<typename T>
concept Printable = requires(T t, std::ostream& os) {
{ os << t } -> std::same_as<std::ostream&>;
};
template<Numeric T>
T add(T a, T b) {
return a + b;
}
template<Printable T>
void print(const T& value) {
std::cout << value << '\n';
}
template<typename T>
concept Container = requires(T container) {
typename T::value_type;
{ container.begin() } -> std::same_as<typename T::iterator>;
{ container.end() } -> std::same_as<typename T::iterator>;
{ container.size() } -> std::convertible_to<std::size_t>;
};
template<Container C>
void process(const C& container) {
for (const & item : container) {
std::cout << item << ;
}
}
Ranges and Views (C++20)
#include <ranges>
#include <vector>
#include <algorithm>
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto even_squares = numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * n; });
for (int value : even_squares) {
std::cout << value << ' ';
}
auto first_three = numbers | std::views::take(3);
auto skip_two = numbers | std::views::drop(2);
auto reversed = numbers | std::views::reverse;
std::vector<std::vector<int>> nested = {{1, 2}, {3, 4}, {5, 6}};
flattened = nested | std::views::join;
std::string text = ;
words = text | std::views::();
lazy = numbers
| std::views::([]( n) { n > ; })
| std::views::([]( n) { n * ; })
| std::views::();
;
Coroutines (C++20)
#include <coroutine>
#include <iostream>
#include <stdexcept>
template<typename T>
class Generator {
public:
struct promise_type {
T current_value;
auto get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
auto initial_suspend() { return std::suspend_always{}; }
auto final_suspend() noexcept { return std::suspend_always{}; }
auto yield_value(T value) {
current_value = value;
return std::suspend_always{};
}
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~() { (handle) handle.(); }
{
handle.();
!handle.();
}
{
handle.().current_value;
}
:
std::coroutine_handle<promise_type> handle;
};
{
a = , b = ;
() {
a;
next = a + b;
a = b;
b = next;
}
}
{
fib = ();
( i = ; i < ; ++i) {
fib.();
std::cout << fib.() << ;
}
}
Smart Pointers
#include <memory>
#include <vector>
class Resource {
int* data;
public:
Resource(int size) : data(new int[size]) {
std::cout << "Resource acquired\n";
}
~Resource() {
delete[] data;
std::cout << "Resource released\n";
}
};
std::unique_ptr<Resource> create_resource() {
return std::make_unique<Resource>(100);
}
auto resource = create_resource();
auto moved = std::move(resource);
std::shared_ptr<Resource> shared = std::make_shared<Resource>(100);
{
std::shared_ptr<Resource> shared2 = shared;
std::cout << "Use count: " << shared.use_count() << '\n';
}
std::weak_ptr<Resource> weak = shared;
if (auto locked = weak.()) {
}
file_deleter = [](FILE* f) { (f) (f); };
;
Move Semantics
#include <utility>
#include <vector>
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {
std::cout << "Constructor\n";
}
~Buffer() {
delete[] data;
std::cout << "Destructor\n";
}
Buffer(const Buffer& other) : size(other.size), data(new int[other.size]) {
std::copy(other.data, other.data + size, data);
std::cout << "Copy constructor\n";
}
Buffer(Buffer&& other) noexcept : size(other.size), data(other.data) {
other.data = nullptr;
other.size = 0;
std::cout << "Move constructor\n";
}
Buffer& operator=(const Buffer& other) {
if (this != &other) {
delete[] data;
size = other.size;
data = new int[size];
std::(other.data, other.data + size, data);
std::cout << ;
}
*;
}
Buffer& =(Buffer&& other) {
( != &other) {
[] data;
data = other.data;
size = other.size;
other.data = ;
other.size = ;
std::cout << ;
}
*;
}
};
{
std::<T>( (std::forward<Args>(args)...));
}
Templates and Metaprogramming
#include <type_traits>
#include <iostream>
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
template<typename T, size_t N>
class Array {
T data[N];
public:
constexpr size_t size() const { return N; }
T& operator[](size_t index) { return data[index]; }
const T& operator[](size_t index) const { return data[index]; }
};
template<typename... Args>
void print(Args... args) {
((std::cout << args << ' '), ...);
std::cout << '\n';
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
abs(T value) {
return value < ? -value : value;
}
< T>
std::enable_if<std::is_floating_point<T>::value, T>::{
std::(value);
}
{
std::cout << std::boolalpha;
std::cout << << std::is_integral_v<T> << ;
std::cout << << std::is_floating_point_v<T> << ;
std::cout << << std::is_pointer_v<T> << ;
}
{
n <= ? : n * (n - );
}
fact10 = ();
std::format (C++20)
#include <format>
#include <iostream>
int main() {
int age = 30;
std::string name = "Alice";
std::cout << std::format("Hello, {}!", name) << '\n';
std::cout << std::format("{1} is {0} years old", age, name) << '\n';
double pi = 3.14159265359;
std::cout << std::format("Pi: {:.2f}", pi) << '\n';
std::cout << std::format("{:<10} {:>10}", "left", "right") << '\n';
int num = 42;
std::cout << std::format("Dec: {0:d}, Hex: {0:x}, Bin: {0:b}", num) << '\n';
std::cout << std::format("{:0>5}", num) << '\n';
return 0;
}
STL Containers
Sequential Containers
#include <vector>
#include <deque>
#include <list>
#include <array>
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);
vec.emplace_back(7);
vec.reserve(100);
std::deque<int> deq = {1, 2, 3};
deq.push_front(0);
deq.push_back(4);
std::list<int> lst = {1, 2, 3};
lst.push_front(0);
lst.push_back(4);
lst.remove(2);
std::array<int, 5> arr = {1, 2, 3, 4, 5};
Associative Containers
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
std::map<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
ages.insert({"Charlie", 35});
std::set<int> numbers = {3, 1, 4, 1, 5, 9};
numbers.insert(2);
std::unordered_map<std::string, int> hash_map;
hash_map["key"] = 42;
std::unordered_set<int> hash_set = {1, 2, 3};
Algorithms
#include <algorithm>
#include <numeric>
#include <vector>
std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7};
std::sort(numbers.begin(), numbers.end());
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
auto it = std::find(numbers.begin(), numbers.end(), 8);
bool found = std::binary_search(numbers.begin(), numbers.end(), 5);
std::vector<int> doubled(numbers.size());
std::transform(numbers.begin(), numbers.end(), doubled.begin(),
[](int n) { return n * 2; });
std::vector<int> evens;
std::copy_if(numbers.begin(), numbers.end(), std::(evens),
[]( n) { n % == ; });
sum = std::(numbers.(), numbers.(), );
product = std::(numbers.(), numbers.(), ,
std::<>());
pivot = std::(numbers.(), numbers.(),
[]( n) { n < ; });
numbers.(std::(numbers.(), numbers.(), ), numbers.());
std::(numbers.(), numbers.());
numbers.(std::(numbers.(), numbers.()), numbers.());
Concurrency
#include <thread>
#include <mutex>
#include <future>
#include <atomic>
void worker(int id) {
std::cout << "Thread " << id << '\n';
}
std::thread t1(worker, 1);
std::thread t2(worker, 2);
t1.join();
t2.join();
std::mutex mtx;
int shared_data = 0;
void increment() {
std::lock_guard<std::mutex> lock(mtx);
++shared_data;
}
std::atomic<int> counter{0};
counter++;
counter.fetch_add(5);
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t([&prom]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value();
});
result = fut.();
t.();
future = std::(std::launch::async, []() {
;
});
value = future.();
Build Systems
CMake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
# Set C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Compiler flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wall -Wextra -Wpedantic -O3)
endif()
# Find packages
find_package(Threads REQUIRED)
find_package(Boost 1.75 REQUIRED COMPONENTS system filesystem)
# Add executable
add_executable(myapp
src/main.cpp
src/module.cpp
include/module.h
)
# Include directories
target_include_directories(myapp PRIVATE include)
# Link libraries
target_link_libraries(myapp PRIVATE
Threads::Threads
Boost::system
Boost::filesystem
)
# Install
install(TARGETS myapp DESTINATION bin)
Best Practices
RAII
void process_file() {
FILE* f = fopen("data.txt", "r");
fclose(f);
}
void process_file() {
auto file = std::unique_ptr<FILE, decltype(&fclose)>(
fopen("data.txt", "r"),
&fclose
);
}
Const Correctness
class Data {
int value;
public:
int get_value() const { return value; }
void set_value(int v) { value = v; }
};
void process(const Data& data) {
int v = data.get_value();
}
Rule of Zero/Three/Five
- Rule of Zero: If you don't manage resources, don't declare special members
- Rule of Three: If you declare destructor, copy constructor, or copy assignment, declare all three
- Rule of Five: Add move constructor and move assignment
Anti-Patterns to Avoid
❌ Raw pointers for ownership: Use smart pointers
❌ Manual memory management: Use RAII
❌ Using C-style arrays: Use std::array or std::vector
❌ Ignoring const correctness: Mark everything const that can be
❌ Unnecessary copies: Use move semantics and references
❌ Premature optimization: Profile before optimizing
❌ Using new without delete: Use smart pointers
Resources