C++23: This and That
I have already written 10 posts about C++23. Today, I want to discuss the features I forgot in the first round.
As a short reminder, here are the main C++23 features.
Main C++23 Features
C++23 offers with deducing this a small but very impactful feature of the core language. Deducing this allows you, similar to Python, to make the implicitly passed this pointer in a member function definition explicit. Thanks to deducing this, a few complex techniques in C++, such as CRTP or the Overload Pattern, will become a piece of cake.
The C++23 library will get many impactful additions. You can directly import the standard library with import std;,
or apply the C++20 format string in std::print
and std::println
. Additionally, we will get flat associative containers such as std::flat_map
for performance reasons. std::flap_map
is a drop-in replacement for std::map
. std::optional
interface is extended with a monadic interface for composability. The new data type std::expected
already has a composable interface and can store an expected or an unexpected value for error handling. Thanks to std::mdspan
, we will get a multidimensional span. Finally, std::generator
is the first concrete coroutine for creating a stream of numbers. std::generator
is part of the ranges library, which will also be improved in C++23.
If you need more details, read my previous posts.
Core Language
- C++23: Deducing This
- C++23: Syntactic Sugar with Deducing This
- C++23: The Small Pearls in the Core Language
- C++23: More Small Pearls
Library
- C++23: A Modularized Standard Library, std::print and std::println
- Range Improvements with C++23
- C++23: A New Way of Error Handling with std::expected
- C++23: A Multidimensional View
- C++23: Range Improvements and std::generator
This is not all.
Fixed Width Floating Point Types
So far, C++ supports the following floating point types:
- no suffix: double
- f or F suffix: float
- l or L suffix: long double
With C++23, we will get five new literal suffixes.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
The table from cppreference shows their types and their properties.
The new data types are defined in the header <stdfloat>
. You can check with the corresponding predefined macro, if your implementation supports the new data types. Then the macro is 1.
Stacktrace
Analyzing the current call stack is often very useful for debugging. The new stack trace library supports precisely this.
The library consists of two classes:
stacktrace_entry
: representation of an evaluation in a stack tracebasic_stacktrace
: a snapshot of the whole stack trace or its given part
Let me put this in simpler words. The stacktrace_entry
class lets you get information about an evaluation in a stack trace. Each stacktrace_entry
object is either empty, or represents an evaluation in a stack trace.
The basic_stacktrace
class is a snapshot of the whole stack trace or its given part.
Still not clear?
In the following simple example, the main
function invokes the func1
function, func1
invokes func2
, and
invokes func2
func3
.
// stacktrace1.cpp #include <iostream> #include <stacktrace> void func3() { std::cout << std::stacktrace::current() << '\n'; } void func2() { func3(); } void func1() { func2(); } int main() { func1(); }
func3
calls the static function current
on std::stacktrace
. std::stacktrace
is an alias for std::basic_stacktrace
with the default allocator.
Executing the program gives you information about the stack trace.
Compiling the program on the Compiler Explorer was pretty challenging. It took me a while to find the appropriate GCC version and the libraries.
Here is the command line for g++ 13.1: g++ -std=c++23 -lstdc++_libbacktrace
.
Thanks to the std::stacktrace_entry
, you can query the stack trace.
// stacktrace2.cpp #include <iostream> #include <stacktrace> void func3() { auto stacktrace = std::stacktrace::current(); for (const auto& entry: stacktrace) { std::cout << "Description: " << entry.description() << '\n'; std::cout << "file: " << entry.source_file() << " and line: " << entry.source_line() <<'\n'; std::cout << '\n'; } } void func2() { func3(); } void func1() { func2(); } int main() { func1(); }
In the function func3, I iterate through the stack trace entries and expressly display its description, file, and line.
I will only write a few words about the remaining C++23 features. The examples are from cppreference.com.
Let me start with two features which may affect the performance of your program.
std::unreachable
You can use std::unreachable
it to mark code that isn’t reachable. When you call std::unreachable,
you’ll get undefined behavior.
// from https://en.cppreference.com/w/cpp/utility/unreachable switch (xy) { case 128: [[fallthrough]]; case 256: [[fallthrough]]; case 512: /* ... */ tex.clear(); tex.resize(xy * xy, Color{0, 0, 0, 0}); break; default: std::unreachable(); }
std::move_only_function
With C++11 we got std::function. std::function
is a polymorphic function wrapper. Therefore, it can take arbitrary callables and give them a name. Callables are all entities that behave like functions, such as functions, function objects, or lambdas.
Read more about std::function
here: Functional in TR1 and C++11.
On the contrary to std::function
, can std::move_only_function
only get a constructible callable. The callable object may be stored in the std::move_only_function
to avoid memory allocation.
// from https://en.cppreference.com/w/cpp/utility/functional/move_only_function auto lambda = [task = std::move(packaged_task)]() mutable { task(); }; // std::function<void()> function = std::move(lambda); // Error std::move_only_function<void()> function = std::move(lambda); // OK
std::byteswap
std::byteswap
swaps the bytes in the value n
. n
must be integral.
The following program applies std::byteswap
.
// from https://en.cppreference.com/w/cpp/numeric/byteswap #include <bit> #include <concepts> #include <cstdint> #include <iomanip> #include <iostream> template<std::integral T> void dump(T v, char term = '\n') { std::cout << std::hex << std::uppercase << std::setfill('0') << std::setw(sizeof(T) * 2) << v << " : "; for (std::size_t i{}; i != sizeof(T); ++i, v >>= 8) std::cout << std::setw(2) << static_cast<unsigned>(T(0xFF) & v) << ' '; std::cout << std::dec << term; } int main() { static_assert(std::byteswap('a') == 'a'); std::cout << "byteswap for U16:\n"; constexpr auto x = std::uint16_t(0xCAFE); dump(x); dump(std::byteswap(x)); std::cout << "\nbyteswap for U32:\n"; constexpr auto y = std::uint32_t(0xDEADBEEFu); dump(y); dump(std::byteswap(y)); std::cout << "\nbyteswap for U64:\n"; constexpr auto z = std::uint64_t{0x0123456789ABCDEFull}; dump(z); dump(std::byteswap(z)); }
Finally, here is the output of the program.
What’s Next?
With my next post, I jump into C++26
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,