Top C++20/23 Features
Tags: cpp
Published: 2023-04-30 20:18:40

1) **Coroutines**:

Coroutines simplify asynchronous and concurrent programming by allowing functions to be suspended and resumed. They use `co_yield`, `co_await`, and `co_return` keywords.

Example:
```c++
#include <iostream>
#include <coroutine>

std::coroutine<int> generator() {
    for (int i = 0; i < 5; ++i) {
        co_yield I;
    }
}

int main() {
    for (int value : generator()) {
        std::cout << value << '\n';
    }
}
```

2) **Ranges**:

Ranges provide a more expressive way to work with sequences of values. The new views namespace contains adaptors that allow for lazy evaluation, improving performance and code readability.

Example:

```c++
#include <iostream>
#include <ranges>
#include <vector>

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

    // Filter even numbers and double their value.
    auto transformed_numbers = numbers | std::views::filter([](int n) { return n % 2 == 0; })
                                       | std::views::transform([](int n) { return n * 2; });

    for (int number : transformed_numbers) {
        std::cout << number << '\n';
    }
}
```

3) **Pattern Matching**:

Pattern matching allows for more concise and expressive ways to deal with complex data structures. The `inspect` keyword is introduced for this purpose.

Example:

```c++
#include <iostream>
#include <variant>

using VarType = std::variant<int, double, std::string>;

void print_value(const VarType& var) {
    inspect (var) {
        int x: std::cout << "Integer: " << x << '\n';
        double y: std::cout << "Double: " << y << '\n';
        std::string s: std::cout << "String: " << s << '\n';
    }
}

int main() {
    VarType a = 42;
    VarType b = 3.14;
    VarType c = "Hello, World!";

    print_value(a);
    print_value(b);
    print_value(c);
}
```

4) **Static Exceptions**:

Static exceptions are a new way to handle errors without the overhead of traditional C++ exceptions. They use the `throws` keyword to declare that a function may return an error.

Example:

```c++
#include <iostream>
#include <stdexcept>
#include <string>

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

    return a / b;
}

int main() {
    auto result = divide(10, 0);
    if (result) {
        std::cout << "Result: " << *result << '\n';
    } else {
        std::cout << "Error: " << result.error() << '\n';
    }
}
```

5) **Concepts and Constraints**:

Concepts allow you to specify constraints on template parameters, making the code more readable and producing better error messages during compilation.

Example:

```c++
#include <iostream>
#include <concepts>

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

template<Numeric T>
T add(T a, T b) {
    return a + b;
}

int main() {
    int result = add(1, 2); // This will compile
    // std::string error = add("Hello", "World"); // This will produce a compilation error
    std::cout << result << '\n';
}
```

6) **constinit**:

The `constinit` keyword guarantees that a variable will be initialized during compile time, improving performance by avoiding the need for dynamic initialization.

Example:

```c++
#include <iostream>

constinit int compile_time_value = 42;

int main() {
    std::cout << "Value: " << compile_time_value << '\n';
}
```

7) **to_underlying**:

The `std::to_underlying` function simplifies converting an enumeration to its underlying type.

Example:

```c++
#include <iostream>
#include <utility>

enum class Color : int {
    Red = 1,
    Green = 2,
    Blue = 3
};

int main() {
    Color c = Color::Red;
    int color_code = std::to_underlying(c);
    std::cout << "Color code: " << color_code << '\n';
}
```

8) **source_location**:

The `std::source_location` class provides a simple way to obtain information about the source code location where it's used, such as file name, line number, and function name.

Example:

```c++
#include <iostream>
#include <source_location>

void print_location(const std::source_location& location = std::source_location::current()) {
    std::cout << "File: " << location.file_name() << '\n'
              << "Line: " << location.line() << '\n'
              << "Function: " << location.function_name() << '\n';
}

int main() {
    print_location();
}
```

9) **flat_set** and **flat_map**:

These new containers store elements in a sorted, contiguous memory region, offering better cache locality and faster lookup times compared to `std::set` and `std::map`.

Example:

```c++
#include <iostream>
#include <flat_set>
#include <string>

int main() {
    std::flat_set<std::string> names = {"Alice", "Bob", "Eve"};

    auto it = names.find("Bob");
    if (it != names.end()) {
        std::cout << "Found: " << *it << '\n';
    }
}
```

10) **simd**:

The new `std::simd` library provides a way to use SIMD (Single Instruction, Multiple Data) operations in C++ programs, allowing for better performance on supported hardware.

Example:

```c++
#include <iostream>
#include <algorithm>
#include <execution>
#include <simd>
#include <vector>

int main() {
    std::vector<float> a = {1.0f, 2.0f, 3.0f, 4.0f};
    std::vector<float> b = {5.0f, 6.0f, 7.0f, 8.0f};
    std::vector<float> c(a.size());

    std::transform(std::execution::simd, a.begin(), a.end(), b.begin(), c.begin(), std::plus<float>());

    for (float value : c) {
        std::cout << value << '\n';
    }
}
```

11) **span** improvements:

`std::span` is a non-owning, bounds-safe view over a contiguous sequence of objects. In C++23, new features have been added, such as the `ssize` function, which returns the size of the span as a signed integer.

Example:

```c++
#include <iostream>
#include <span>
#include <vector>

void print_positive_size(const std::span<int>& sp) {
    std::cout << "Size: " << std::ssize(sp) << '\n';
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::span<int> num_span(numbers);

    print_positive_size(num_span);
}
```

12) **make_shared**:

C++23 adds support for creating `std::shared_ptr` instances for arrays using `std::make_shared`.

Example:

```c++
#include <iostream>
#include <memory>

int main() {
    auto array_ptr = std::make_shared<int[]>(5);

    for (int i = 0; i < 5; ++i) {
        array_ptr[i] = i;
    }

    for (int i = 0; i < 5; ++i) {
        std::cout << array_ptr[i] << '\n';
    }
}
```

13) **contains**:

C++23 adds `std::contains` to various standard library containers, simplifying the process of checking whether a container contains a specific value.

Example:

```c++
#include <iostream>
#include <unordered_set>

int main() {
    std::unordered_set<int> numbers = {1, 2, 3, 4, 5};

    if (numbers.contains(3)) {
        std::cout << "3 is in the set." << '\n';
    }
}
```

14) **erase_if**:

C++23 introduces `std::erase_if`, a utility function to remove elements from a container based on a specific condition.

Example:

```c++
#include <iostream>
#include <vector>
#include <algorithm>

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

    std::erase_if(numbers, [](int n) { return n % 2 == 0; });

    for (int number : numbers) {
        std::cout << number << ' ';
    }
}
```

15) **chrono** improvements:

C++23 introduces new functions and features to the `std::chrono` library, such as calendar and timezone support.

Example:

```c++
#include <iostream>
#include <chrono>
#include <format>

int main() {
    using namespace std::chrono;

    // Get the current time in the system's local timezone
    zoned_time local_time{current_zone(), system_clock::now()};

    // Output the formatted local time
    std::cout << "Local time: " << std::format("{:%Y-%m-%d %H:%M:%S}", local_time) << '\n';
}
```

16) **Lambda** improvements:

C++23 introduces improvements to lambda expressions, such as making them more expressive and easier to use.

Example:

```c++
#include <iostream>
#include <vector>
#include <algorithm>

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

    // A lambda with a template parameter
    auto print_value = []<typename T>(const T& value) {
        std::cout << value << ' ';
    };

    std::for_each(numbers.begin(), numbers.end(), print_value);
}
```

17) **net**:

C++23 introduces a new networking library, std::net, which provides support for sockets, address resolution, and more.

Example:

```c++
#include <iostream>
#include <net>

int main() {
    std::net::ip::tcp::iostream stream("www.example.com", "http");
    if (!stream) {
        std::cerr << "Error: " << stream.error().message() << '\n';
        return 1;
    }

    stream << "GET / HTTP/1.1\r\n"
           << "Host: www.example.com\r\n"
           << "Connection: close\r\n\r\n";
    stream.flush();

    std::string line;
    while (std::getline(stream, line)) {
        std::cout << line << '\n';
    }
}
```

18) New algorithms in **algorithm**:

C++23 adds new algorithms to the `std::algorithm` library, such as `shift_left` and `shift_right`, which shift elements within a range.

Example:

```c++
#include <iostream>
#include <vector>
#include <algorithm>

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

    std::shift_left(numbers.begin(), numbers.end(), 2);

    for (int number : numbers) {
        std::cout << number << ' ';
    }
}
```

19) **identity**:

The `std::identity` function object is introduced in C++23, which can be useful in generic programming as a simple pass-through function.

Example:

```c++
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

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

    std::transform(numbers.begin(), numbers.end(), numbers.begin(), std::identity{});

    for (int number : numbers) {
        std::cout << number << ' ';
    }
}
```

20) **bulk_execute**:

C++23 adds new functions to the `std::execution` library, such as `std::execution::bulk_execute`, which allows you to run a function multiple times concurrently.

Example:

```c++
#include <iostream>
#include <execution>
#include <thread>

void print_hello(size_t index) {
    std::cout << "Hello from task " << index << '\n';
}

int main() {
    std::execution::bulk_execute(std::execution::par, print_hello, 10);
}
```

21) **counted_iterator**:

C++23 introduces `std::counted_iterator`, which is useful for creating an iterator that keeps track of a remaining count.

Example:

```c++
#include <iostream>
#include <iterator>
#include <vector>

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

    auto start = std::counted_iterator(numbers.begin(), 5);
    auto end = std::default_sentinel;

    for (auto it = start; it != end; ++it) {
        std::cout << *it << ' ';
    }
}
```

22) **default_initializable**:

C++23 introduces the `std::default_initializable` concept, which helps to ensure that a type can be default-initialized.

Example:

```c++
#include <iostream>
#include <concepts>

template <std::default_initializable T>
T get_default() {
    return T{};
}

int main() {
    int default_int = get_default<int>();
    std::cout << "Default int: " << default_int << '\n';
}
```

23) **starts_with** and **ends_with**:

C++23 adds `starts_with` and `ends_with` member functions to `std::basic_string`, making it easier to check if a string starts or ends with a specific substring or character.

Example:

```c++
#include <iostream>
#include <string>

int main() {
    std::string greeting = "Hello, world!";

    if (greeting.starts_with("Hello")) {
        std::cout << "The greeting starts with 'Hello'." << '\n';
    }

    if (greeting.ends_with('!')) {
        std::cout << "The greeting ends with an exclamation mark." << '\n';
    }
}
```

24) **is_scoped_enum**:

C++23 adds the `std::is_scoped_enum` type trait, which helps determine if a given type is a scoped enumeration (i.e., an `enum class`).

Example:

```c++
#include <iostream>
#include <type_traits>

enum class ScopedEnum { A, B, C };
enum UnscopedEnum { X, Y, Z };

int main() {
    std::cout << std::boolalpha
              << "ScopedEnum is scoped: " << std::is_scoped_enum_v<ScopedEnum> << '\n'
              << "UnscopedEnum is scoped: " << std::is_scoped_enum_v<UnscopedEnum> << '\n';
}
```

25) **chunk**:

C++23 introduces `std::views::chunk`, which allows you to create a view that groups elements in a range into subranges of a specified size.

Example:

```c++
#include <iostream>
#include <vector>
#include <ranges>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto chunked_view = numbers | std::views::chunk(3);

    for (const auto& chunk : chunked_view) {
        for (int number : chunk) {
            std::cout << number << ' ';
        }
        std::cout << '\n';
    }
}
```

26) **unwrap_reference**:

C++23 introduces `std::unwrap_reference`, a utility to unwrap reference_wrapper instances and leave other types unchanged.

Example:

```c++
#include <iostream>
#include <functional>
#include <type_traits>

int main() {
    int value = 42;
    std::reference_wrapper<int> ref_wrapper(value);

    // Unwrap reference_wrapper
    auto& unwrapped_ref = std::unwrap_reference(ref_wrapper);

    // Modify the original value through the unwrapped reference
    unwrapped_ref = 55;

    std::cout << "Original value: " << value << '\n';
}
```

27) **copyable** and **movable**:

C++23 introduces the `std::copyable` and `std::movable` concepts, which help ensure that a type is copyable or movable, respectively.

Example:

```c++
#include <iostream>
#include <concepts>

template <std::copyable T>
void process_copyable(const T& obj) {
    // Process obj
}

template <std::movable T>
void process_movable(T&& obj) {
    // Process obj
}

int main() {
    int value = 42;
    process_copyable(value); // OK, int is copyable
    process_movable(std::move(value)); // OK, int is movable
}
```

28) **polymorphic_allocator** improvements:

C++23 enhances `std::polymorphic_allocator` with additional features, such as support for array allocations.

Example:

```c++
#include <iostream>
#include <memory_resource>
#include <vector>

int main() {
    std::pmr::unsynchronized_pool_resource pool;
    std::pmr::polymorphic_allocator<int> alloc(&pool);

    std::pmr::vector<int> numbers(alloc);
    numbers.reserve(10);
    for (int i = 1; i <= 10; ++i) {
        numbers.push_back(i);
    }

    for (int number : numbers) {
        std::cout << number << ' ';
    }
}
```

29) **dynamic_extent**:

C++23 introduces `std::dynamic_extent`, which is a constant that represents a dynamic extent for span and mdspan.

Example:

```c++
#include <iostream>
#include <span>
#include <vector>

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    std::span<int, std::dynamic_extent> dynamic_span(numbers);

    for (int number : dynamic_span) {
        std::cout << number << ' ';
    }
}
```

30) **to_underlying**:

C++23 introduces `std::to_underlying`, a utility function that makes it easier to convert an enumeration value to its underlying integral type.

Example:

```c++
#include <iostream>
#include <type_traits>

enum class Color : int { Red, Green, Blue };

int main() {
    Color color = Color::Red;

    auto underlying_value = std::to_underlying(color);

    std::cout << "Color::Red has an underlying value of: " << underlying_value << '\n';
}
```