So if you watch this channel, you know that I start every single episode with,
"Hello, and welcome to C++ Weekly. I'm your host Jason
Turner. I am available for code reviews and on-site training."
A bit ago, a few months, perhaps, I did an episode, and I said something about
avoiding the call to move because that is better,
"and if you don't understand why, then you
should have me to your company for training,"
when a lot of you thought that that seemed a bit
condescending that I would say that in the episode.
So I've decided to give in to your complaints and do a very
short episode as to why you should write code that avoids move.
I promise you, this is going to be a very short look at this.
And if this catches your interest, you should
have me to your company for on-site training.
[Music]
This episode is sponsored by
JetBrains. "To keep up with the pace of progress in the
C++ ecosystem, we need tools that help us work quickly and effectively every day.
CLion is a cross-platform C and C++ IDE by JetBrains that makes this challenge its
main goal, integrating code
analysis, refactoring, and the best ecosystem tools
natively into your day-to-day development workflows.
JetBrains has just recently launched CLion Nova, a version of CLion with a C++
language engine from ReSharper C++ and JetBrains Writer.
It addresses long-standing performance and quality issues
and unifies the user experience across all our C++ tools.
Learn more and use it for free. Check out the link in the description."
Let's go ahead and make the utility class that I like to always make that helps us
understand what our code is doing with object lifetimes.
[Silence]
Alright, I've just created one of my little lifetime objects and it has created
exactly one and destroyed exactly one of these.
Again, well, it would be so cool if you just went and bought one of my Object
Lifetime puzzler books from Amazon, which you
can get a nice print copy from and fill them out.
It talks about object lifetime a lot in various ways and they're really fun to
do, so check that out in the video description;
but I've created one of these things. If I create two of
them, then I have two constructors and two destructors called.
This is very straightforward.
Now I'm going to create an array of these things.
An array of two, to be exact.
And it created two and destroyed two exactly like we might have expected it to do.
Now I'm going to add a little bit of
member data to this just to illustrate a point.
So if I were to do something like this...
and now I want to initialize this array of things for some reason with these
values, then I can initialize it with l1 and l2.
You're going to see here that I create two
of these things, that is line 26 and line 29,
and we don't have any debug statements
printed for the updates of their values here.
Then I'm doing a copy of those elements into this
array and then I have four items that need to be destroyed.
Should not be surprising to most viewers.
Now you say, "Duh, obviously, if I need to do something like this, I should avoid
that copy by moving these elements into the array."
That only half solves our problem.
We still have this object, l1, that was default constructed,
then we have l2, which was default constructed,
and then we've got two move constructors - that is
here when I'm initializing the elements of the array.
Then I still have four destructors to be called.
Now these destructors are pretty trivial.
All they do is print something out,
and if you have an object that is in fact trivially
destructible, then that literally does nothing upon destruction,
and that's great,
but if you have anything even a little bit more complex,
such as a vector or a string, that destructor does something.
It does something even in the case where the object is empty.
That is, it calls delete or free on the null
pointer that it has, or it has to do an explicit check.
Usually, I don't think they do an explicit check,
they just call free because calling delete on a null
pointer, I believe, is perfectly well-defined behavior,
and hen they just let the runtime sort out the
difference, which it's going to have to do anyhow.
Okay, so that gets us halfway there.
This is the code that I told you to avoid.
Let's go ahead and make one little helper here.
So I just created this lambda;
I'm getting an error that it's not being used, and then I'm getting errors that I
don't have these l1s and l2 to make anymore.
All right, so now I have exactly two objects that exist in this code.
You think that you see four, but you are wrong.
The object called l that exists inside this lambda ends up being the exact same
object that is created inside the array using direct initialization.
The copy and move elision rules from the standard, as well as the as-if rule, come
together and they work nicely along with a feature that every compiler since 1998
has implemented called "named return value optimization."
I don't really like calling it that at all
because it's really just a feature of how compilers work,
but again, that's the kind of thing that I have to make a living off of,
so I do think it would be great if you had me to your company for training.
But this code, this code that avoided move and relies on understanding that in this
simple "one object constructed, one object returned" function case,
we are almost guaranteed return value optimization.
Basically, every compiler does this anyhow.
Well, yeah, let's just go ahead and demonstrate this.
This is with -O3. I'm just going to make a point and put
it in -O0 mode with GCC just in case you don't believe me.
Yeah, look, it's still exactly the same.
Writing code that relies on simple, composable
functions instead of relying on std::move is always better.
That is the simple answer here.
Of course, hire me for onsite training for a deeper conversation about this topic,
and check out episode 125, The Optimal Way To Return From A Function;
it talks about this exact same thing.
So that does it for this episode. Be sure to subscribe. Give this video a thumbs
up, a like; share it with your friends, family,
coworkers, strangers you happen to pass on the street,
and I'll catch you in the next episode of C++ Weekly.