Hello, and welcome to C++ Weekly. I'm your host,
Jason Turner. I'm available for code reviews and on-site training.
In this episode I'm going to do a really quick overview of std::ref and std::cref. This comes
down to something that was asked as a requested episode on my list of episodes, so if you are
interested in recommending your own episodes or voting on which episodes we do next make sure you
check out the links in the video description for a link to where you can suggest episode ideas.
[Music] Now of course do make sure you scroll down. Check out t-shirts,
check out links to my upcoming workshops, which currently I have scheduled for
Norway, September 18th and 19th at NDC TechTown, so be sure to check that out. std::ref
and std::cref - both of these are used as shortcuts for creating reference wrappers.
They were originally added for basically the scenario where
you're passing to something that expects a copy but you don't want to make a copy.
Now let's just go ahead and look over here at the Compiler Explorer output. I have bound this
function i to my local i. I am incrementing it a couple of times; I'm calling bound,
and I expect that main should return 2, but it is not. It returns zero and why? That is because
std::bind is actually taking a copy of each of the parameters that are passed to it. So a shorthand
for how to say, "I know that you're expecting a copy but please in fact actually take a reference"
is to use std::ref to create a reference wrapper around that thing. Now if I were to have called
cref then I'm going to get a const reference, and then that's going to fail to compile because
you can't mutate a const. So that did its job. If you didn't have this helper function
then you would find yourself doing something like this, which is just a little bit more annoying
than necessary. So that's what it is, it's a shorthand helper for creating a reference wrapper.
Now this can also be used for the case where I am creating a
struct that I want to hold on to a reference. Now this also cannot be null,
just like a regular C++ reference cannot be, but it avoids some of the problems that I
think I've mentioned in other episodes. If I have a reference member here it
doesn't really do what you expect it to do for things like 'move' and assignment operations
and that kind of thing, and so having a reference wrapper instead of a reference member - definitely
better because reference member deletes some of these special member overloads that we
might want. Now a third place where you might use this is in the case of calling algorithms,
specifically the standard algorithms. Now there are definitely better ways to do this,
so don't take this as a very good example, but let's just go ahead and demonstrate.
The further I get into this example the more I think that it's a bad plan,
but we will finish the demonstration regardless at this point.
Okay, I have a mutable lambda that I'm passing into my 'for each' function. It is to accumulate
all of these values, and I can get the current accumulated values by asking it to accumulate 0
back in to this lambda, which is why this is not a great plan, but you will see that this
actually does (I still) return o from main. The reason it's returning 0 from main is
because all of the algorithms actually take their callable by copy, so I can actually say I want a
copyable thing that is actually a reference to a thing and use std::ref in this regard as well.
Then I get 15 printed here because I've accumulated over the values 1, 2, 3, 4, 5,
which should be 15. So this is one of the, I think, least known and least appreciated
features of reference wrapper and how std::ref can be used. That capability is possible because
it has its own callable paren operator overload that takes a set of arguments and just passes
them through to the reference wrapper thingy. This is actually one of the original purposes
for reference wrapper. Now each algorithm has its own rules about whether or not it makes sense to
pass in a callable by reference like this, like if it's mutating do you have any guarantee what
order things are going to be called in? So again, not meant to be a great example here,
but just an illustration of what std::ref, std::cref and reference wrappers can be used
for. I do recommend if you need a reference-like thing inside of your struct that you strongly
consider reference wrapper instead of a pointer that has to be checked all the time or a regular
C++ reference which breaks some of our 'copy' and 'move' semantics as we would expect them to work.
So thanks for watching this episode of C++ Weekly. I hope you enjoyed it. Be sure to subscribe.
[Music]