Hello and welcome to C++ Weekly. I'm your host, Jason Turner. I'm available for code
reviews and on-site training. Now I recently did an episode that showed 90 Topics in 20 minutes,
and I fully appreciate that that was a lot. Hopefully some of you were inspired by that
episode and you decided to really start to dig in and understand C++ a little bit more through the
topics that I presented. Now for those of you who were overwhelmed by it, I am going to give
the three-step process that I would recommend for actually leveling up your C++ knowledge.
[Music]
So before we continue with this episode, I do again want to remind you to check out those links
and notes in the description for this video. It shows you how to get more involved in the channel,
how to support the channel, how to buy T-shirts, all kinds of cool things that
you should definitely check out. So the first step in really developing your C++
knowledge is to create a tool that helps you understand object lifetime, and I did this...
oh, it's probably one of the first things that I did when I really started to want to
understand C++ more, and in C++11 or above the tool would look something like this:
Now we have this handy little tool, and we can start to do experiments
that really challenge our understanding of lifetime and return value optimization and,
well, all kinds of interesting things. For example, if we were to do something like this,
then we might be surprised to see that there is exactly one lifetime object created and
exactly one lifetime object destroyed. There is no assignment operator called,
there is no copy or move anywhere. You might call me out and say "This is at -O3,"
and I would say, "Yeah, technically you are correct." Let's go ahead and take that down
to -O0 and we can see that it doesn't change anything. So this starts to make us challenge our
assumptions and understand the language more. Now part two once we have this is to study the lambda,
and this is going directly back to that 90 topics in 20 minutes episode that you've probably already
seen. If you haven't you really should go watch that, but we can start to do things like this
and see that if we create a lambda that returns one of these things it doesn't output anything,
right? We haven't done anything yet. If we call it, then it still creates exactly one
object and destroys exactly one object even though we never did anything with
that object. The compiler still had to honor that that's what we asked it to do,
and if we do something like this where we use a generalized lambda capture
to construct a lifetime object as part of our capture set, then we see that we have one called
and one destroyed again, interestingly, but we're not actually executing the lambda at this point.
As you move this thing around and decide, "Well I'm going to put one in the body of the lambda,
and you know what, I'm not even going to name it, I'm just going to put it here in the body
of my lambda," now when I call the lambda I can see that I had two constructed. One was the one
through this call chain of f3, and one was the one that was this object right here on the stack. (Now
if you think that this is even kind of fun then you really should check out my Object Lifetime
Puzzler book which is absolutely linked to in the video description.) So as you move this
around and start to get a feel for what exactly the compiler has to do in all of these different
situations and what the runtime is executing (as we can see we only have one being created here),
now if I capture this, then I'm going to get a capture by copy; and if I do something like this
then, well, the code might get a little bit confusing to you, but you might start to
understand a little bit better how object lifetime works, what a lambda is, what the compiler is
doing inside of here, what the runtime has to do, what the optimizer can or cannot do. It all really
helps to solidify your knowledge of C++. So now we have this outstanding understanding of the lambda,
object lifetime, what a lambda is, how it stores objects, what it means to do something like copy a
lambda, like, what does that mean? Where did this copy come from? Which object is being copied in
here in fact? And you can step through all of this in a debugger or whatever. So our first step was
to create a tool that helps you understand object lifetime; the second step is to study the lambda,
and then this really should just be a playground - I want you to have fun. Then step three is to
create your own std::function implementation. This is not a small ask. I have already done an
episode on this - you can go back and reference that episode and understand that once you have
created your std::function you have a better understanding of type erasure, inheritance,
object lifetime, heap versus stack, like all these things; just opens the floodgates to all
kinds of concepts about C++. I want to say that I am specifically recommending this because I
know that a large portion of my C++ knowledge came from implementing the scripting engine ChaiScript,
which I don't talk about very often on the channel these days, but it's a scripting engine that I
created many years ago. I came to the realization recently that the things that I really needed to
learn to implement ChaiScript had a strong over overlap with the things that you need
to know to implement std::function. Now this is going to need to be something that can work with
lambdas, free functions, member functions, static member functions - on and on. There's lots of
details here that you need to do to be able to get something like a fully functioning std::function
implementation working, and then your bonus point extra credit comes in two flavors. The first is
to make your std::function implementation constexpr capable, which should be very
doable as of C++20, and then your fifth thing is to implement small function optimization.
Those are the extra credit assignments. These are the main things - create this tool, study the
lambda using that tool, and create a std::function implementation. You could have a lot of fun if
you're in C++20 with this tool by utilizing things like source_location, or if you are in C++23
then you could utilize the stack trace library. (It) should be a lot of fun. Hopefully you think
this looks like fun. So to get you started I'm going to link to this little tool that I created
that runs inside of Compiler Explorer. It has a lot going on. It has address sanitizer, it has
undefined behavior sanitizer, it has all kinds of object lifetime tracing things that it can output
for you. It uses libformat. There's all kinds of crazy examples with vectors and copying of
vectors; and you could trace through all of this and see and understand what 'sort' is doing, how
it's moving objects around, how many copies and moves happen. You get a print out at the end of
the program saying what happened. You could step into this with a debugger if you wanted to, and it
also has clang-tidy running, although clang-tidy seems to be not very happy with me right now in
Compiler Explorer. So you're not necessarily going to get that clang-tidy output that is so handy
to tell you if you've done something wrong, but I tried to set you up with all of the tools
here that make this a easy, fun to use little playground. So there we go. Yeah, if I comment
out some of it then I get clang-tidy. We can see that actually running this whole pipeline here
is a little slow, but if you could recreate it on your own, in your own instance of Compile Explorer
or in your IDE, that'd be cool too. They will be a link to this and hopefully this helps get you
started. So I hope you enjoyed this episode of C++ Weekly on how to really level up your C++ game. I
hope I made this more approachable than the 90 Topics in 20 minutes episode, but I do still
recommend that you watch that one. So thanks for watching C++ Weekly. You know what to do - give
us a thumbs up, be sure to subscribe. Leave a comment and I'll catch you in the next episode.
[Music]