# New C++23 features I'm excited about


Work on c++23 standardisation is well in progress and we already have a couple
of new features to play with.  [Toolchain support](https://en.cppreference.com/w/cpp/compiler_support/23) varies but some early testing
is already possible.  I've prepared a list of features that I, personally
appreciate a lot and which most definitely will improve my code.  Let's go
through them.

## CppCon overview

There's a set of great *CppCon* talks regarding new additions coming with c++23.
Amongst others, a nice, concise and to the point overview is probably best
presented by [Marc Gregoire](https://www.youtube.com/watch?v=b0NkuoUkv0M).

## Features I like

Below is a short list of things, I think are definitely a good way forward.

### [P0881R7](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0881r7.html) - builtin support for stacktraces

This is something very much welcomed.  A builtin, standardised support for
stacktrace generation is a great tool when debugging crashes or software
problems.  I'm sure that this feature will be embraced by crash reporting
software and CI systems as well to provide more details about potential
problems.

Stacktraces, could be generated prior to raising an assertion in
debug versions of the builds to provide more context to the bug.  This will
dramatically improve the debugging efforts as, most of the time, when
investigating a crash or a bug, obtaining a backtrace is crucial to understand
the nature of the problem.

It seems like the implementation is gonna be based on Boost.StackTrace which
seems like a reasonable choice that will guarantee sufficient quality and
robustness.  Obtaining stacktraces will be as simple as:

```C++
#include <stacktrace>
...
void foo() {
    auto st = std::stacktrace::current();
    std::cout << st << std::endl;
}
```

How does it work in practice?  I wasn't able to verify the stacktrace library
so far.  I've briefly tried with gcc 12.2.0 shipped with Arch Linux but, during
the compilation of a trivial example, the compiler core dumped (heh :)).  Version of
`clang` shipped with Arch Linux is complaining about problems within the
stacktrace library.  This is something I definitely will verify once again.

```
cd $HOME/dev/cpp23 && clang++ -std=c++2b ./stacktraces.cpp -lstdc++_libbacktrace && ./a.out
In file included from ./stacktraces.cpp:1:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/stacktrace:632:3: error: no matching function for call to 'operator delete'
                _GLIBCXX_OPERATOR_DELETE (static_cast<void*>(_M_frames),
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/stacktrace:589:35: note: expanded from macro '_GLIBCXX_OPERATOR_DELETE'
# define _GLIBCXX_OPERATOR_DELETE __builtin_operator_delete
                                  ^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/new:144:6: note: candidate function not viable: no known conversion from 'unsigned long' to 'const std::nothrow_t' for 2nd argument
void operator delete(void*, const std::nothrow_t&) _GLIBCXX_USE_
```

#### Implementation status

- ❌ gcc 12.2.0 - crashes when building
- ❌ clang 14.0.6 - fails to build

### [P0288R9](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0288r9.html) std::move_only_function<>

This is a great addition.  Prior to it, all function objects were copied when
passed as arguments.  This also means that lambdas capturing move only objects
could not be passed around by value.  This changes entirely with `std::move_only_function<>`.

```C++
int foo(std::move_only_function<int(int)> f) {
  return f(123);
}

int main() {
  std::cout << foo([c = std::make_unique<int>(1)](int i) { return *c + i; }) << std::endl;
  return 0;
}

```

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P0323R12](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0323r12.html) std::expected

This is a new thing very similar to `Result` object in rust.  In a way,
`std::expected` introduces new error handling paradigm.  `std::expected`
wraps a return value and an error in case the returning function failed to
complete.  Here's a simple piece of code that demonstrates this in action:

```C++
std::expected<int, std::string> foo(int n, int d) {

  if (d == 0) {
    return std::unexpected("Division by zero");
  }

  return n / d;
}

int main(int argc, const char* argv[]) {
  if (auto r = foo(123, 0); r.has_value()) {
    std::cout << r.value() << std::endl;
  } else {
    std::cout << "Error occured: " << r.error() << std::endl;
  }
  return 0;
}

```

There's also `value_or()` which allows to conveniently retrieve the expected
value or a provided default in case the `std::expected` contains an error.

`value()` called on an `std::expected` containing an error will result in
`std::bad_expected_access` being thrown.

It would be great to have a similar set of monadic operations for
`std::expected` as proposed with `std::optional`.  Programmers with rust
background will also miss language syntax for error propagation.  Still though,
it's a great addition and definitely a step in the right direction.

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P2128R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2128r6.pdf) Multidimensional subscript operator

P2128R6 allows for arbitrary number of arguments to `operator[]`.  Which is a
great change.  I've already read some articles calling `operator[]` a new call
operator in disguise, since the semantics is now quite similar.  Regardless,
this is a great change which most likely will simplify a lot of code
implementing operations on multidimensional data.  In current C++ implementation most of the time, implementations relied on pairs of tuples to support multiple index arguments i.e:

```c++
T operator[](std::pair<std::size_t, std::size_t> idxs);
```

With C++23 it's gonna be a lot simpler:

```C++
T operator[](std::size_t i, std::size_t j);

```

And yes, you can have multiple overloads if required:

```C++
T operator[](std::size_t i) {
    // implementation for single index
}

T operator[](std::size_t i, std::size_t j) {
    // implementation for two indices
}

... and so on
```

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P0330R8](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0330r8.html) std::size_t literals

New suffix literals for `std::size_t` are a great small detail that allows to
avoid implicit conversion to `int`.  To be precise, the change introduces the
following suffixes:

- `z`/`Z` for signed integer type corresponding to `std::size_t`
- `uz`/`zu` for `std::size_t`
- `t` for `std::ptrdiff_t`
- `ut`/`tu` for unsigned integer type corresponding to `std::ptrdiff_t`

This is something I see myself relying on very often.  The examples in the
proposal draft illustrate the need for these suffixes very clearly.

In case of `std::min`, `std::max` I can't even count how many times I had to
`static_cast` the constants or explicitly declare the function types.  This can
now be avoided.

```C++
#include <algorithm>
#include <vector>

int main() {
	std::vector<int> v;
	/* work with v... */

	std::size_t clamped_space = std::max(0zu,
		std::min(54zu, v.size()) // error without the `zu` suffix
	);

	return 0;
}
```

Same goes, when declaring more than one variable in for loop or initialisation
expressions:

```C++
for (auto i = 0zu; i < v.size(); ++i) {
	std::cout << i << ": " << v[i] << '\n';
}
```

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P0627R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0627r6.pdf) std::unreachable()

This one is pretty self explanatory.  It allows to explicitly mark unreachable
code which often happens at the end of the function returning a value, like this one:

```c++
#include <utility>

...

int foo(int i) {
  if (i >= 0) {
    return i;
  } else {
    return i + 1;
  }

  std::unreachable();
}
```

This should silence any potential "missing return value" warnings and, at least
for me, makes the code more readable and self-documenting.

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P0798R6](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r6.html) Monadic std::optional operations

Oh no, we've got the "M" word.  Hopefully, if you read my post about
[functional parsing](https://twdev.blog/2022/09/parser_combinators/) (which I strongly recommend) then you should have a
general understanding what a  monad is (or rather what it does).  I'm gonna be
bashed by this explanation but still gonna take the risk, in short, monadic
operation transforms the value category from one to another - in case of parser
combinators, this was a transformation from a parser of type "a" to a parser of
type "b" (Parser A -> Parser B).  In case of `std::optional` it will be a
transformation from `std::optional<A>` to `std::optional<B>` using a given
transformation function... and that's what this change is all about.

We'll now have the following new functions that allow to chain operations on
optionals:

- `transform`
- `and_then`
- `or_else`

`transform` is purely monadic and meant to indeed transform from
`std::optional<A>` -> `std::optional<B>`.

`and_then` - allows to compose/chain functions returning optionals

`or_else` - allows to call a function when optional bears no value

Here's an example:

```
std::optional<int> divide(int n, int d) {
  if (d != 0) {
    return n / d;
  }

  return {};
}

int main() {
  auto result = divide(10,0)
    .or_else([]() -> std::optional<int>{ std::cerr << "division failed" << std::endl; return {}; })
    .and_then([](auto v) -> std::optional<int> { return v * 10; })
    .transform([](auto v) { return std::to_string(v); });

  std::cout << "result: " << result.value() << std::endl;
  return 0;
}

```

Best thing about this approach is that the chain will be short-circuited should
the optional contain no value at any stage.

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6


### [P1938R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1938r3.html) consteval conditions

This is a huge change which allows to conveniently bridge the build and
run time execution.  It's now possible to explicitly check, if execution
happens during build time and control the flow:

```C++

if consteval {
    // this will happen during build time
} else {
    // this will happen in runtime
}

```

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P2216R3](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2216r3.html) std::format improvements & print header

This addition provides a new header `<print>` with functions like:

- `std::print`
- `std::println`

Initially, I thought that it's a bit gimmicky but actually this feels pretty
cool.  I'm about to find out in practice how good it works and with what
caveats it comes but I see myself using these functions often.

#### Implementation status

- ❌ gcc 12.2.0
- ❌ clang 14.0.6

## Features I don't like

### [P2334R1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2334r1.pdf) New preprocessor directives

This proposal adds two new pre-processor directives:

- `#elifdef`
- `#elifndef`

These are to simplify the ubiquitous:

- `#elif defined()`
- `#elif !defined()`

Honestly, I don't see much value in that and considering the fact that there
were hopes to deprecate preprocessor use in C++ altogether I find these rather
superfluous.

I'm worried as well that this will cause further code fragmentation and in
order to provide as much portability as possible, we will avoid these anyway as
much as we can.

#### Implementation status

- ✔️  gcc 12.2.0
- ✔️ clang 14.0.6

### [P0849R8]( https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0849r8.html) out_ptr

This change solves a real, existing problem (or rather inconvenience) in many
code bases that interface with C APIs that take a pointer as a return value.
Consider the following:

```C++
void allocate_linked_list(Node** n);
void deallocate_node(Node* n);

Node* head;
allocate_linked_list(&head);
// we've got a head to our linked list now
...
deallocate_node(head);
```

The question is how to interface that with smart pointers?  The usual approach
looks something like:

```C++
std::unique_ptr<Node, decltype(&deallocate_node)> head(nullptr, &deallocate_node);
Node* tmp;
allocate_linked_list(&tmp);
head.reset(tmp);

// head is now managed by a smart_ptr
```

Granted, this isn't the prettiest and requires an extra temporary pointer to
adopt the interface between the linked list allocation API and the smart
pointer.  Therefore, it is proposed to introduce a wrapper that would perform
all these steps behind the scenes, like so:

```c++
...
allocate_linked_list(std::inout_ptr(head));
// head is now managed by a smart_ptr
```

I find it... redundant.  The name `inout_ptr` is pretty bad IMHO and very poorly 
conveys the intentions, additionally it hides the details unnecessarily which
from my point of view are quite important for code readability.

#### Implementation status

- ❌ gcc 12.2.0
- ❌ clang 14.0.6

## Conclusion

C++23 brings a lot of great changes to the language.  It's definitely something
I look forward to.  I'm still waiting for the introduction of networking which
is a piece of puzzle C++ is definitely missing out of the box.

