| name | cpp-modern |
| description | Modern C++ development principles and decision-making. C++23/C++26, RAII, modern idioms, performance optimization. |
C++ Modern - C++23/C++26
Modern C++ development principles and decision-making.
Learn to THINK, not copy-paste C++98 patterns.
⚠️ How to Use This Skill
This skill teaches decision-making principles for modern C++.
- Use C++20/23 features, not C++98/03
- Prefer RAII and smart pointers
- Don't write C with classes
1. Standard Selection
Decision Tree
What are you building?
│
├── System/Network Programming
│ ├── C++23/26 (latest features)
│ └── Boost.Asio (async I/O)
│
├── Game Engine / Graphics
│ └── C++23 + GPU compute (CUDA/HIP)
│
├── Embedded
│ ├── C++17 (wide compiler support)
│ └── C++14 (for very constrained)
│
├── Libraries (Reusable)
│ ├── C++20 (modules, concepts)
│ └── Header-only (keep simple)
│
├── Desktop Applications
│ ├── Qt + C++17/20
│ ├── SDL2/ImGui (games/tools)
│ └── wxWidgets
│
└── Web Assembly (WASM)
└── C++23 compiled to WASM
Compiler Support
| Feature | C++17 | C++20 | C++23 | C++26 |
|---|
| Modules | No | ✅ Partial | ✅ Most | ✅ Full |
| Concepts | No | ✅ | ✅ | ✅ |
| Ranges | No | ✅ | ✅ | ✅ |
| Coroutines | No | ✅ | ✅ | ✅ |
| static constexpr | ✅ | ✅ | ✅ | ✅ |
2. Modern C++ Features
Concepts (C++20)
template<typename T, typename = std::enable_if_t<
std::is_integral_v<T>>>
T doubleIt(T val) { return val * 2; }
template<std::integral T>
T doubleIt(T val) { return val * 2; }
template<typename T>
requires std::integral<T>
T multiply(T a, T b) { return a * b; }
Ranges (C++20)
#include <ranges>
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<int> doubled;
std::for_each(numbers.begin(), numbers.end(),
[&](int n) { doubled.push_back(n * 2); });
auto doubled = numbers
| std::views::filter([](auto n) { return n > 0; })
| std::views::transform([](auto n) { return n * 2; });
for (auto n : doubled) {
std::cout << n << " ";
}
std::optional (C++17)
#include <optional>
User* findUser(int id) {
if () return nullptr;
return &user;
}
std::optional<User> findUser(int id) {
if (notFound) return std::nullopt;
return User{id, "John"};
}
auto user = findUser(1);
if (user) {
std::cout << user->name;
}
std::cout << findUser(99).value_or(User{-1, "Guest"}).name;
std::variant (C++17)
#include <variant>
struct Error { std::string msg; }
struct Success { int value; }
using Result = std::variant<Error, Success>;
Result process(int input) {
if (input < 0) return Error{"Negative not allowed"};
return Success{input * 2};
}
std::visit([](auto&& r) {
using T = std::decay_t<decltype(r)>;
if constexpr (std::is_same_v<T, Error>) {
std::cerr << "Error: " << r.msg << "\n";
} else {
std::cout << "Success: " << r.value << "\n";
}
}, result);
3. Smart Pointers & RAII
Ownership Patterns
#include <memory>
auto up = std::make_unique<MyClass>(args);
auto sp1 = std::make_shared<MyClass>();
auto sp2 = sp1;
std::weak_ptr<MyClass> wp = sp1;
if (auto locked = wp.lock()) {
}
MyClass* p = new MyClass();
RAII Pattern
class FileGuard {
std::FILE* file;
public:
explicit FileGuard(const char* path)
: file(std::fopen(path, "r")) {}
~FileGuard() {
if (file) std::fclose(file);
}
FileGuard(const FileGuard&) = delete;
FileGuard& operator=(const FileGuard&) = delete;
};
void processFile() {
FileGuard guard("data.txt");
}
4. Modern async
std::jthread (C++20)
#include <thread>
std::jthread worker{[](std::stop_token st) {
while (!st.stop_requested()) {
doWork();
std::this_thread::sleep_for(100ms);
}
}};
}
Coroutines (C++20)
std::future<int> computeAsync(int x) {
return std::async(std::launch::async, [x]() {
return x * x;
});
}
5. Containers & Algorithms
Container Selection
| Scenario | Best Container | Why |
|---|
| Random access | std::vector | Cache-friendly |
| Insertions at front | std::deque | O(1) front |
| Fast search | std::unordered_set | O(1) average |
| Ordered iteration | std::set | O(log n) |
| LIFO stack | std::stack | Adaptor |
| FIFO queue | std::queue | Adaptor |
Views (C++20) - No Copy!
std::vector<int> data{1,2,3,4,5};
auto v1 = data | std::views::reverse;
auto v2 = v1 | std::views::drop(1);
auto v3 = v2 | std::views::transform([](int x) {
return x * 2;
});
for (int x : v3) std::cout << x << " ";
6. Testing Patterns
Testing Frameworks
#include <gtest/gtest.h>
class MathTest : public ::testing::Test {
protected:
Calculator calc;
};
TEST_F(MathTest, AddTwoNumbers) {
EXPECT_EQ(calc.add(2, 3), 5);
EXPECT_NE(calc.add(2, 3), 6);
}
TEST_F(MathTest, DivideByZero) {
EXPECT_THROW(calc.divide(1, 0), std::invalid_argument);
}
Testing Tools
| Tool | Purpose |
|---|
| GoogleTest | Unit testing |
| Catch2 | Header-only testing |
| Doctest | Lightweight |
| google/benchmark | Benchmarks |
7. Safety & Performance
Bounds Safety
std::vector<double> data(9);
std::mdspan<double, std::dextent_t<2, 3>> matrix(data.data(), 3, 3);
std::vector<int> vec{1, 2, 3};
int x = vec.at(5);
Performance Tips
| Don't Do | Do Instead |
|---|
vector for tiny fixed-size | std::array |
| Pass by value for large types | Pass by reference/pointer |
| Premature optimization | Measure first |
| Raw new (except C interop) | make_unique / make_shared |
| Copying large objects | std::move or std::span |
8. Decision Checklist
Before implementing:
❌ Anti-Patterns to Avoid
❌ DON'T:
- Use C++98 patterns (manual new/delete)
- Use raw pointers for ownership
- Prefer raw arrays (
int arr[10])
- Use
using namespace std; in headers
- Skip const correctness
- Use exception handling for flow control
✅ DO:
- Use C++20/23 features where supported
- Use
std::vector as default container
- Use
constexpr/consteval for compile-time
- Use
std::span for array views
- Use concepts for constraints
- Use ranges over loops
Remember: Modern C++ is about safety and expressiveness.
Use C++20/23 features and RAII patterns.
Think in modern C++.