Modern C++ Features (C++11 to C++23)

repository·master·Indexed 12 days ago

https://github.com/anthonycalandra/modern-cpp-features

A curated reference of modern C++ features from C++11 to C++23, organized by language version and feature type. Covers key additions such as C++23's consteval if, deducing this, and std::expected; C++20's Concepts, Coroutines, std::format, and the spaceship operator (<=>); as well as library utilities like std::span, std::jthread, and std::osyncstream.

Tokens
30.3K
Snippets
153
Records
161
Agent score
96%

What's inside Modern C++ Features

  1. Overview of C++20 Language and Library Features

    master

    C++20 introduces significant enhancements to both the language syntax and the standard library.

    Language Features

    • Coroutines: Support for asynchronous programming patterns.
    • Concepts: Constraints for template arguments to improve error messages and interface design.
    • Three-way comparison: The spaceship operator (<=>).
    • Designated initializers: Explicitly naming members during struct initialization.
    • Template syntax for lambdas: Allowing template parameters in lambda expressions.
    • Range-based for loop with initializer: Adding a scope-limited variable within the loop header.
    • [[likely]] and [[unlikely]] attributes: Providing hints to the compiler for branch prediction.
    • Deprecate implicit capture of this: Improving safety in lambda captures.
    • Class types in non-type template parameters: Allowing more complex types as template arguments.
    • constexpr virtual functions: Enabling virtual functions to be evaluated at compile time.
    • explicit(bool): Controlling implicit conversions for boolean types.
    • Immediate functions: Using consteval for compile-time execution.
    • using enum: Bringing enum members into the surrounding scope.
    • lambda capture of parameter pack: Capturing variadic template packs in lambdas.
    • char8_t: A distinct type for UTF-8 characters.
    • constinit: Ensuring variables are initialized at compile time.
    • VA_OPT: Handling empty arguments in variadic macros.
  2. Overview of C++20 Library Features

    master

    The C++20 standard library adds several utilities for formatting, concurrency, and type manipulation:

    • Text formatting: Enhanced string formatting capabilities.
    • Concepts library: Standardized concept definitions.
    • Synchronized buffered outputstream: Thread-safe buffered I/O.
    • std::span: A non-owning view over a contiguous sequence of objects.
    • Bit operations: New utilities for bit manipulation.
    • Math constants: Standardized mathematical constants (e.g., pi).
    • std::is_constant_evaluated: Checking if a function is being executed in a constant expression context.
    • std::make_shared supports arrays: Ability to create shared pointers to arrays.
    • starts_with and ends_with: String view/string prefix and suffix checks.
    • Check if associative container has element: Improved element lookup methods.
    • std::bit_cast: Type-safe bit-level reinterpretation of values.
    • std::midpoint: Calculating the midpoint of two values without overflow.
    • std::to_array: Converting initializer lists to std::array.
    • std::bind_front: A more efficient alternative to std::bind.
    • Uniform container erasure: Standardized way to erase elements from containers.
    • Three-way comparison helpers: Utilities for <=> operations.
    • std::lexicographical_compare_three_way: Comparing ranges using three-way comparison.
    • std::jthread: A joining thread that automatically joins on destruction.
    • Safe integral comparisons: Utilities to prevent signed/unsigned comparison issues.
  3. Overview of C++11 Language and Library Features

    master
    C++11 introduced significant improvements to both the language syntax and the standard library. Key language features include move semantics, variadic templates, lambda expressions, and auto type deduction. Key library features include smart pointers, threading support (std::thread, std::async), the std::chrono time library, and new container types like std::array and unordered containers.
  4. Overview of C++17 features

    master

    C++17 introduced several significant language and library enhancements.

    Key Language Features include:

    • Class template argument deduction (CTAD)
    • Declaring non-type template parameters with auto
    • Folding expressions
    • New rules for auto deduction from braced-init-lists
    • constexpr lambdas
    • Lambda capture this by value
    • Inline variables
    • Nested namespaces
    • Structured bindings
    • Selection statements with initializer
    • constexpr if
    • UTF-8 character literals
    • Direct-list-initialization of enums
    • Attributes: [[fallthrough]], [[nodiscard]], [[maybe_unused]]
    • __has_include macro

    Key Library Features include:

    • Type-safe unions and optional values: std::variant, std::optional, std::any
    • String handling: std::string_view
    • Function invocation: std::invoke, std::apply
    • Filesystem: std::filesystem
    • Low-level data: std::byte
    • Container improvements: Splicing for maps and sets
    • Parallelism and Algorithms: Parallel algorithms, std::sample, std::reduce, prefix sum algorithms, gcd and lcm, std::not_fn
    • Utilities: std::clamp, string conversion to/from numbers, and rounding functions for chrono durations/timepoints.
  5. Integrate C APIs with smart pointers using `std::out_ptr` and `std::inout_ptr`

    master

    To bridge C-style APIs (which use pointer-to-pointers like T**) with C++ smart pointers, use std::out_ptr and std::inout_ptr.

    • std::out_ptr(smart_ptr): Creates a temporary pointer-to-pointer that updates the smart pointer (e.g., via reset()) when it goes out of scope. Use this when a C API writes a new pointer to you.
    • std::inout_ptr(smart_ptr): Similar to out_ptr, but used when the C API both reads and writes the pointer.

    These abstractions safely manage memory lifetime even if exceptions are thrown and support implicit casts to void**.

    // Example: Using std::out_ptr with a C API
    std::unique_ptr<MyHandle, resource_deleter> resource(nullptr);
    int err = c_api_create_handle(std::out_ptr(resource));
    // `resource` now owns the memory allocated within `c_api_create_handle`.
    
    // Example: Using std::inout_ptr with a C API
    std::shared_ptr<MyHandle> resource(nullptr);
    int err = c_api_recreate_handle(std::inout_ptr(resource), resource_deleter{});
    // `resource` now shares the memory allocated within `c_api_recreate_handle`.
  6. Manage memory with Smart Pointers

    master

    C++11 introduced several smart pointers to manage heap memory automatically:

    • std::unique_ptr: A non-copyable, movable pointer that owns its resource. Use std::make_unique (C++14) for safer allocation.
    • std::shared_ptr: A pointer that manages a resource shared across multiple owners via a reference-counted control block. Access to the control block is thread-safe, but access to the managed object itself is not.
    • std::weak_ptr: Used to observe a std::shared_ptr without contributing to the reference count.
    std::unique_ptr<Foo> p1 { new Foo{} };  // `p1` owns `Foo`
    if (p1) {
      p1->bar();
    }
    
    {
      std::unique_ptr<Foo> p2 {std::move(p1)};  // Now `p2` owns `Foo`
      f(*p2);
    
      p1 = std::move(p2);  // Ownership returns to `p1` -- `p2` gets destroyed
    }
  7. Use constexpr for Compile-Time Constants

    master

    The constexpr specifier indicates that a variable or function is a constant expression, meaning it can (and often should) be evaluated by the compiler at compile-time. This can lead to more efficient code by embedding results directly into the binary.

    constexpr int square(int x) {
      return x * x;
    }
    
    constexpr int a = square(2); // Evaluated at compile-time
  8. How Coroutines work in C++20

    master

    Coroutines are special functions that can suspend and resume execution. They are identified by the use of co_return, co_await, or co_yield. C++20 coroutines are stackless, meaning their state is typically allocated on the heap.

    Common patterns include:

    • Generators: Functions that use co_yield to produce a sequence of values one by one.
    • Tasks: Asynchronous computations that use co_await to suspend execution until a result is ready.

    Note: Since generator and task are not yet in the standard library, libraries like cppcoro are often used to implement these types.

    // Generator example
    generator<int> range(int start, int end) {
      while (start < end) {
        co_yield start;
        start++;
      }
    }
    
    // Task example
    task<void> echo(socket s) {
      for (;;) {
        auto data = co_await s.async_read();
        co_await async_write(s, data);
      }
    }
  9. Deduce types with `decltype(auto)`

    master

    While auto deduces types by value (stripping references and cv-qualifiers), decltype(auto) deduces the type while preserving references and cv-qualifiers. This is highly useful in generic code where you want to return exactly what the expression evaluates to (e.g., a const int& or an int&&).

    const int x = 0;
    auto x1 = x;           // int
    decltype(auto) x2 = x; // const int
    
    int y = 0;
    int& y1 = y;
    auto y2 = y1;          // int
    decltype(auto) y3 = y1; // int&
    
    int&& z = 0;
    auto z1 = std::move(z);    // int
    decltype(auto) z2 = std::move(z); // int&&
  10. Deduce object type with Deducing `this`

    master

    Instead of writing multiple overloads for const and non-const member functions, you can use the this keyword in the first parameter of a member function. This allows the compiler to deduce the object's type and value category (e.g., const or non-const) automatically.

    // NEW WAY USING DEDUCING THIS:
    struct T {
      decltype(auto) operator[](this auto& self, std::size_t idx) { 
        return self.mVector[idx]; 
      }
    };
  11. Improved range-based `for` safety in C++23

    master

    C++23 introduces fixes for lifetime issues in range-based for loops. It prevents certain patterns that were broken in previous standards where the expression being iterated over would produce a temporary object that expired before the loop body executed.

    Examples of previously broken patterns now fixed in C++23 include:

    • for (auto e : getTmp().getRef())
    • for (auto e : getVector()[0])
    • for (auto valueElem : getMap["key"])
    • for (auto e : get<0>(getTuple()))
    • for (auto e : getOptionalCollection().value())
    • for (char c : get<std::string>(getVariant()))
  12. Expand constexpr function capabilities in C++14

    master

    In C++14, constexpr functions are no longer limited to a single return statement. You can now use common control flow structures like if statements, multiple return statements, and loops within a constexpr function.

    constexpr int factorial(int n) {
      if (n <= 1) {
        return 1;
      } else {
        return n * factorial(n - 1);
      }
    }
    factorial(5); // == 120