Hello, and welcome to C++ Weekly. I'm your host, Jason Turner. I'm available for code
reviews and on-site training. Well in this episode we're going to talk about another
new C++23 feature that you might not have known that you needed, so be sure to stick around.
I just wanted to make sure that you're aware that every single video description
has links to upcoming events, important information about this particular video,
how to buy any of my puzzle books or my Best Practices book, how to hire me for training,
how to become a patron, how to support this channel in general. Alright,
so let's take this humble example of using std::function wrapper. Now,
you've hopefully seen the previous episodes where I talk about what exactly std::function is
and a simple way that it can be implemented, but let's go ahead and look at, you know,
a use case where we have some sort of need for this type erased wrapper. I'm not going to pretend
like I'm going to create any kind of real example here, but let's just do something like this.
It's a function that takes a std::function to register some sort of callback,
and I want to pass a callback to this function to register it. So I think, "Okay, I'm going
to go ahead and construct this thing from a lambda," and my lambda, for terrible reasons,
needs a unique pointer in it. This is really not code that you're supposed to actually do,
just for the record, but we're gonna do something like a make_unique of the value 42,
and now we need a function that takes an int and returns an int.
Again, please don't actually do this,
but we need this for our example. Now this is a good opportunity for me to point out that
you should check out those links in the video description to check out t-shirts,
swag, books, all the different ways that you can support the channel.
What we have here is code that fails to compile, and the reason
it fails to compile is because std::function requires a function object that is copyable,
and a unique pointer is not copyable. If I really wanted to I could use make_shared here,
which means that my lambda is going to own a shared pointer which can be copied, and now this
code can execute. It generates a shocking amount of code for something that should be quite simple.
Let's go ahead and complete the loop here and call this register_callback function.
So this compiles. It can't execute because I never implemented register_callback. I
don't need to register that for the sake of this example, we just need to kind of
get a grip on why the C++23 feature that I'm getting ready to introduce exists,
and that feature is called move_only_function. I can create this as a move_only_function,
and now I can go back to make_unique here, and what we'll see is this code can't work
because I'm now passing it a copy of this callback function but a move-only function, interestingly,
cannot be copied, it can only be moved. So I can put in a std::move here
of the local cb variable, and I can pass that to this function, and we can see that this generates
way less code because, first of all, we don't have a shared pointer anymore, and second of all,
we don't have to generate all of the code that is necessary internal to the std::function to
know how to copy its type erased element that it owns. Again, you should go watch that episode on
what std::function is to make sure that you understand why that would be a complicated
thing to do. Now I want to point out also that I state in my Best Practices that you really
should 'const' everything you can. Well now this code can't compile because if you try to move a
'const' object you get a copy. You can't move a constant. That is illogical. You would have to
mutate the thing that you're moving, and you can't do that; so you end up doing this, but then you've
broken one of my Best Practices on not making locals that are not 'const'. So what do you do?
The solution is actually quite simple. If you know that this thing needs to immediately be sunk into
some sort of function that takes it as a sink, then we do this. We eliminate the question of
the 'const', we eliminate the question of the move and we eliminate the question of
"What state was this local cb variable in a moment ago." So it kind of cleans up everything
and makes me considerably happier than I was a moment ago.
So there you have it. C++23's move_only_function was created to solve, in part, the problem of
function objects that are not copyable such as those that own unique pointers which are
themselves not copyable. So thank you for watching this episode of C++ Weekly. Be sure to comment.
Give this video a thumbs up and subscribe.