cpp-modern-features

Master modern C++ features from C++11 through C++23, including lambdas, move semantics, ranges, concepts, and compile-time evaluation. This skill enables you to write efficient, expressive, and maintainable modern C++ code.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cpp-modern-features" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-cpp-modern-features

C++ Modern Features

Master modern C++ features from C++11 through C++23, including lambdas, move semantics, ranges, concepts, and compile-time evaluation. This skill enables you to write efficient, expressive, and maintainable modern C++ code.

C++11 Features

Auto Type Deduction

The auto keyword enables automatic type inference, reducing verbosity and improving maintainability:

// Traditional std::vector<int>::iterator it = vec.begin(); std::map<std::string, std::vector<int>>::const_iterator map_it = mymap.find("key");

// Modern C++11 auto it = vec.begin(); auto map_it = mymap.find("key");

// Auto with initialization auto value = 42; // int auto pi = 3.14; // double auto name = std::string("Alice"); // std::string auto lambda = [](int x) { return x * 2; }; // lambda type

Range-Based For Loops

Simplified iteration over containers and arrays:

std::vector<int> numbers = {1, 2, 3, 4, 5};

// Traditional loop for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) { std::cout << *it << " "; }

// Range-based for (C++11) for (int num : numbers) { std::cout << num << " "; }

// With references to modify elements for (int& num : numbers) { num *= 2; }

// With const references for efficiency for (const auto& str : string_vector) { process(str); }

Lambda Expressions

Anonymous functions with capture capabilities:

// Basic lambda auto add = [](int a, int b) { return a + b; }; int result = add(3, 4); // 7

// Lambda with captures int multiplier = 10; auto multiply = [multiplier](int x) { return x * multiplier; };

// Capture by reference int counter = 0; auto increment = &counter { counter++; };

// Capture all by value auto func1 = = { return x + y + z; };

// Capture all by reference auto func2 = & { x++; y++; };

// Mixed captures auto func3 = [&sum, multiplier](int x) { sum += x * multiplier; };

// Mutable lambda (can modify captured values) auto mutable_func = counter mutable { return counter++; };

Smart Pointers

Automatic memory management with RAII:

#include <memory>

// unique_ptr - exclusive ownership std::unique_ptr<int> ptr1 = std::make_unique<int>(42); std::unique_ptr<MyClass> obj = std::make_unique<MyClass>(arg1, arg2);

// shared_ptr - shared ownership with reference counting std::shared_ptr<int> ptr2 = std::make_shared<int>(100); std::shared_ptr<int> ptr3 = ptr2; // Reference count = 2

// weak_ptr - non-owning reference std::weak_ptr<int> weak = ptr2; if (auto locked = weak.lock()) { // Use locked pointer safely std::cout << *locked << std::endl; }

// Custom deleter auto deleter = [](FILE* fp) { fclose(fp); }; std::unique_ptr<FILE, decltype(deleter)> file(fopen("data.txt", "r"), deleter);

Nullptr

Type-safe null pointer constant:

// Old way (ambiguous) void func(int x); void func(char* ptr); func(NULL); // Which overload? Depends on NULL definition

// Modern way (unambiguous) func(nullptr); // Clearly calls func(char*)

// Smart pointer initialization std::unique_ptr<int> ptr = nullptr; if (ptr != nullptr) { // Use ptr }

C++14 Features

Generic Lambdas

Lambdas with auto parameters:

// Generic lambda (C++14) auto generic_add = [](auto a, auto b) { return a + b; };

int sum1 = generic_add(3, 4); // Works with int double sum2 = generic_add(3.14, 2.71); // Works with double std::string concat = generic_add(std::string("Hello"), std::string(" World"));

// Multiple auto parameters auto compare = [](auto a, auto b) { return a < b; };

// Use in algorithms std::vector<int> vec = {5, 2, 8, 1, 9}; std::sort(vec.begin(), vec.end(), [](auto a, auto b) { return a > b; });

Return Type Deduction

Automatic return type for functions:

// C++14: auto return type deduction auto multiply(int a, int b) { return a * b; // Return type deduced as int }

auto get_vector() { return std::vector<int>{1, 2, 3}; // Deduced as std::vector<int> }

// Multiple return statements must have same type auto conditional(bool flag) { if (flag) return 42; // int else return 100; // int - OK // return 3.14; // ERROR: different type }

Make Unique

Factory function for unique_ptr:

// C++11 required manual construction std::unique_ptr<MyClass> ptr1(new MyClass(arg1, arg2));

// C++14 provides std::make_unique auto ptr2 = std::make_unique<MyClass>(arg1, arg2);

// Exception safety benefit func(std::unique_ptr<int>(new int(1)), std::unique_ptr<int>(new int(2))); // Risky func(std::make_unique<int>(1), std::make_unique<int>(2)); // Safe

C++17 Features

Structured Bindings

Decompose objects into individual variables:

// Pairs and tuples std::pair<int, std::string> get_data() { return {42, "Answer"}; }

auto [id, name] = get_data(); std::cout << id << ": " << name << std::endl;

// Maps std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}}; for (const auto& [name, score] : scores) { std::cout << name << " scored " << score << std::endl; }

// Arrays int arr[3] = {1, 2, 3}; auto [a, b, c] = arr;

// Structs struct Point { int x, y; }; Point p{10, 20}; auto [x, y] = p;

If Constexpr

Compile-time conditional statements:

template<typename T> auto process(T value) { if constexpr (std::is_integral_v<T>) { return value * 2; } else if constexpr (std::is_floating_point_v<T>) { return value * 3.14; } else { return value; } }

int result1 = process(10); // Returns 20 double result2 = process(2.0); // Returns 6.28

Fold Expressions

Variadic template operations:

// Sum all arguments template<typename... Args> auto sum(Args... args) { return (args + ...); }

int total = sum(1, 2, 3, 4, 5); // 15

// Print all arguments template<typename... Args> void print(Args... args) { (std::cout << ... << args) << std::endl; }

print("Value: ", 42, " ", 3.14); // Value: 42 3.14

// Logical operations template<typename... Args> bool all_true(Args... args) { return (... && args); }

bool result = all_true(true, true, false); // false

Std::optional

Type-safe optional values:

#include <optional>

std::optional<int> find_value(const std::vector<int>& vec, int target) { auto it = std::find(vec.begin(), vec.end(), target); if (it != vec.end()) { return *it; } return std::nullopt; }

// Usage std::vector<int> numbers = {1, 2, 3, 4, 5}; if (auto result = find_value(numbers, 3)) { std::cout << "Found: " << *result << std::endl; } else { std::cout << "Not found" << std::endl; }

// Value_or for default int value = find_value(numbers, 10).value_or(-1);

Std::variant

Type-safe union:

#include <variant>

std::variant<int, double, std::string> data;

data = 42; data = 3.14; data = std::string("Hello");

// Visit pattern std::visit([](auto&& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, int>) { std::cout << "int: " << arg << std::endl; } else if constexpr (std::is_same_v<T, double>) { std::cout << "double: " << arg << std::endl; } else { std::cout << "string: " << arg << std::endl; } }, data);

// Get value if (auto* str = std::get_if<std::string>(&data)) { std::cout << *str << std::endl; }

C++20 Features

Concepts

Constrain template parameters:

#include <concepts>

// Define a concept template<typename T> concept Numeric = std::is_arithmetic_v<T>;

// Use concept to constrain template template<Numeric T> T add(T a, T b) { return a + b; }

// Concept with requires clause template<typename T> concept Hashable = requires(T a) { { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>; };

// Multiple constraints template<typename T> concept Sortable = std::copyable<T> && requires(T a, T b) { { a < b } -> std::convertible_to<bool>; };

// Use in function template<Sortable T> void sort_data(std::vector<T>& vec) { std::sort(vec.begin(), vec.end()); }

Ranges

Composable algorithms and views:

#include <ranges>

std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

// Filter and transform using views auto result = numbers | std::views::filter([](int n) { return n % 2 == 0; }) | std::views::transform([](int n) { return n * n; });

for (int n : result) { std::cout << n << " "; // 4 16 36 64 100 }

// Take first N elements auto first_five = numbers | std::views::take(5);

// Reverse view auto reversed = numbers | std::views::reverse;

// Chaining multiple operations auto complex_view = numbers | std::views::filter([](int n) { return n > 3; }) | std::views::transform([](int n) { return n * 2; }) | std::views::take(3);

Coroutines

Cooperative multitasking:

#include <coroutine> #include <iostream>

// Simple generator struct Generator { struct promise_type { int current_value;

    Generator get_return_object() {
        return Generator{std::coroutine_handle&#x3C;promise_type>::from_promise(*this)};
    }

    std::suspend_always initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    std::suspend_always yield_value(int value) {
        current_value = value;
        return {};
    }

    void return_void() {}
    void unhandled_exception() {}
};

std::coroutine_handle&#x3C;promise_type> handle;

Generator(std::coroutine_handle&#x3C;promise_type> h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }

int value() { return handle.promise().current_value; }
bool next() {
    handle.resume();
    return !handle.done();
}

};

Generator counter(int start, int end) { for (int i = start; i < end; ++i) { co_yield i; } }

// Usage auto gen = counter(0, 5); while (gen.next()) { std::cout << gen.value() << " "; }

Three-Way Comparison (Spaceship Operator)

Simplified comparison operators:

#include <compare>

struct Point { int x, y;

// Single operator generates all six comparison operators
auto operator&#x3C;=>(const Point&#x26; other) const = default;

};

Point p1{1, 2}; Point p2{1, 3};

bool eq = (p1 == p2); // false bool lt = (p1 < p2); // true bool gt = (p1 > p2); // false

// Custom spaceship operator struct Person { std::string name; int age;

auto operator&#x3C;=>(const Person&#x26; other) const {
    if (auto cmp = name &#x3C;=> other.name; cmp != 0)
        return cmp;
    return age &#x3C;=> other.age;
}

};

C++23 Features Preview

Std::expected

Error handling without exceptions:

#include <expected>

std::expected<int, std::string> divide(int a, int b) { if (b == 0) { return std::unexpected("Division by zero"); } return a / b; }

// Usage auto result = divide(10, 2); if (result) { std::cout << "Result: " << *result << std::endl; } else { std::cout << "Error: " << result.error() << std::endl; }

// Transform and error handling auto transformed = divide(20, 4) .transform([](int x) { return x * 2; }) .or_else([](auto error) { std::cout << "Error: " << error << std::endl; return std::expected<int, std::string>(0); });

If with Initializer Enhancement

// C++23: if with pattern matching improvements std::map<int, std::string> map = {{1, "one"}, {2, "two"}};

if (auto [it, inserted] = map.insert({3, "three"}); inserted) { std::cout << "Inserted: " << it->second << std::endl; }

// Enhanced static constexpr if template<typename T> void process(T value) { if constexpr (requires { value.size(); }) { std::cout << "Size: " << value.size() << std::endl; } }

Move Semantics

Rvalue References

class Buffer { char* data; size_t size;

public: // Constructor Buffer(size_t s) : size(s), data(new char[s]) {}

// Copy constructor
Buffer(const Buffer&#x26; other) : size(other.size), data(new char[size]) {
    std::copy(other.data, other.data + size, data);
}

// Move constructor
Buffer(Buffer&#x26;&#x26; other) noexcept : size(other.size), data(other.data) {
    other.data = nullptr;
    other.size = 0;
}

// Copy assignment
Buffer&#x26; operator=(const Buffer&#x26; other) {
    if (this != &#x26;other) {
        delete[] data;
        size = other.size;
        data = new char[size];
        std::copy(other.data, other.data + size, data);
    }
    return *this;
}

// Move assignment
Buffer&#x26; operator=(Buffer&#x26;&#x26; other) noexcept {
    if (this != &#x26;other) {
        delete[] data;
        data = other.data;
        size = other.size;
        other.data = nullptr;
        other.size = 0;
    }
    return *this;
}

~Buffer() { delete[] data; }

};

Std::move and Perfect Forwarding

#include <utility>

// Using std::move std::vector<int> vec1 = {1, 2, 3, 4, 5}; std::vector<int> vec2 = std::move(vec1); // vec1 is now empty

// Perfect forwarding template<typename T> void wrapper(T&& arg) { // Forward arg preserving its value category process(std::forward<T>(arg)); }

// Factory function with perfect forwarding template<typename T, typename... Args> std::unique_ptr<T> make_unique_custom(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Emplace methods use perfect forwarding std::vector<std::pair<int, std::string>> vec; vec.emplace_back(1, "one"); // Constructs in-place

Lambda Advanced Features

Recursive Lambdas

// C++14 and later auto fibonacci = [](int n) { auto impl = [](int n, auto& self) -> int { if (n <= 1) return n; return self(n-1, self) + self(n-2, self); }; return impl(n, impl); };

// C++23: Deducing this auto fibonacci_cpp23 = [](this auto self, int n) -> int { if (n <= 1) return n; return self(n-1) + self(n-2); };

Init Captures (C++14)

// Move-only types in captures auto ptr = std::make_unique<int>(42); auto lambda = p = std::move(ptr) { std::cout << *p << std::endl; };

// Initialize new variable in capture auto lambda2 = value = computeExpensiveValue() { return value * 2; };

// Multiple init captures auto lambda3 = x = getValue(), y = getOtherValue() { return x + y; };

Compile-Time Evaluation

Constexpr Functions

// C++11 constexpr (limited) constexpr int factorial_cpp11(int n) { return (n <= 1) ? 1 : n * factorial_cpp11(n - 1); }

// C++14 constexpr (more powerful) constexpr int factorial(int n) { int result = 1; for (int i = 2; i <= n; ++i) { result *= i; } return result; }

constexpr int value = factorial(5); // Computed at compile time

// C++20 constexpr with std::vector constexpr auto compute_primes(int max) { std::vector<int> primes; for (int i = 2; i < max; ++i) { bool is_prime = true; for (int p : primes) { if (i % p == 0) { is_prime = false; break; } } if (is_prime) primes.push_back(i); } return primes; }

Consteval (C++20)

Functions that must be evaluated at compile time:

consteval int square(int n) { return n * n; }

constexpr int value1 = square(5); // OK: compile-time // int x = 5; // int value2 = square(x); // ERROR: must be compile-time

// Consteval with constexpr constexpr int maybe_compile_time(int n) { if (std::is_constant_evaluated()) { return n * n; } else { return n + n; } }

Best Practices

  • Prefer auto for complex types: Use auto to avoid verbose type declarations, especially with iterators and lambdas

  • Use range-based for when possible: Clearer intent and less error-prone than traditional loops

  • Prefer lambdas for simple operations: Especially in algorithm calls like std::transform and std::for_each

  • Use smart pointers for ownership: Prefer unique_ptr and shared_ptr

over raw pointers for owned resources

  • Embrace move semantics: Implement move constructors/assignment for resource-owning types

  • Use structured bindings: Makes code more readable when working with pairs, tuples, and maps

  • Prefer std::optional over special values: Use std::optional instead of sentinel values like -1 or nullptr

  • Use concepts to constrain templates (C++20): Makes template errors clearer and documents requirements

  • Leverage ranges and views (C++20): More composable and efficient than traditional algorithms

  • Use constexpr for compile-time computation: Move computation to compile-time when possible for better performance

Common Pitfalls

  • Dangling references with auto: auto& can create dangling references to temporaries

  • Capturing by reference in lambdas: Reference captures can outlive the captured variables

  • Moving from const objects: std::move on const objects doesn't actually move

  • Default capturing [=] or [&]: Can accidentally capture more than intended

  • Forgetting to mark move operations noexcept: Prevents some optimizations in containers

  • Using std::move after using an object: Moved-from objects are in valid but unspecified state

  • Range-based for with temporaries: Container temporaries destroyed before loop body

  • Optional value access without checking: Using * or value() without verifying has_value()

  • Variant access without checking: std::get throws if wrong type; use get_if or visitor

  • Overusing auto: Can hide important type information; use judiciously

When to Use

Use this skill when:

  • Working with modern C++ codebases (C++11 and later)

  • Refactoring legacy code to use modern features

  • Implementing resource management with RAII

  • Writing generic code with templates and lambdas

  • Optimizing performance with move semantics

  • Implementing type-safe optional or variant types

  • Working with ranges and functional-style algorithms

  • Creating compile-time computed values

  • Applying concepts to constrain templates

  • Learning or teaching modern C++ practices

Resources

  • C++ Reference - Lambda

  • C++ Reference - Auto

  • C++ Reference - Range-based for

  • C++ Reference - Smart Pointers

  • C++ Reference - Move Semantics

  • C++ Reference - Structured Bindings

  • C++ Reference - Optional

  • C++ Reference - Variant

  • C++ Reference - Concepts

  • C++ Reference - Ranges

  • C++ Reference - Coroutines

  • C++ Reference - Constexpr

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-type-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

c-systems-programming

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cpp-templates-metaprogramming

No summary provided by upstream source.

Repository SourceNeeds Review