Hello, and welcome to C++ Weekly. I'm your host, Jason Turner. Once a week I pick some
topic of interest in C++ and dig into it with some live coding. In this episode I'm going to
give an overview of the 'std::bind' function and its capabilities. So if you look at the
documentation for std::bind it seems a bit obtuse, maybe simplistic. So it takes a thing called 'f'
that is a "callable", which means a function object, pointer to function, reference to pointer
(a reference to function), pointer to a member function, pointer data member... It's anything
that can be called as if it were a function and then it takes a list of arguments that want to
be bound to this function call. So in the most simplistic sense it binds parameters to functions.
Let's say for instance that we have this function called 'print' and it takes an integer, and we're
going to do it by const reference to make a point later; and we simply want to print out this value.
So we can create a new callable thing, and that's what callable,
excuse me, that's what 'bind' does - it takes a callable object and returns a new callable one;
and if you noticed in the documentation it says the return value is unspecified. It's basically
unknowable and implementation defined. So we need to use 'auto' to capture one of these,
and we can do something like "bind, print, 5," and now we should get every time we call
'f' the value 5 printed to the screen... and sure enough. We can also do something like "i=5" here,
and bind the value 'i' thusly and we get the same result. Now the point
that I started to make earlier is if we reassign 'i' the value 6 and rerun this,
we still only get 5 printed out; and that's because one of the things that
'bind' guarantees that we'll do is make a copy of the arguments past in. So if we wanted the
value to actually track we would need to make a standard reference wrapper object for it.
Now when we execute, we get 5 & 6, which is what we would expect based on what we're doing here,
and based on the fact that this takes a reference. Now it is also interesting to note just how long
this takes to compile. That's 0.6 seconds and 58 Meg's of RAM for a quite trivial test case.
So 'bind' is not a very lightweight thing, and it is definitely something that should be used
to judiciously. So what else can bind do? If we wanted, for example, this to take multiple
arguments (and let's take this back to not being a reference because that's a little silly) but
let's say we do want it to also print a string. We might do something like this,
and now what we can do is say, "Well, we want the first value to be bound to this variable 'i',
but we want the second one to be specified by the user." So now 'f' is a callable thing that takes
one parameter, and we could do something like "hello world," and we get "hello world." Notice
that our 5 and 6 is still tracking, and that's because it is actually the thing returned by
std::bind that is owning the standard reference object. (Oops.) So we didn't need the parameter
to take a reference because 'bind' internally is going to handle the reference as we would
expect it to. So looking further, 'bind' lets you do crazy things like reorder parameters.
So we can say pass the second parameter as if it were the first parameter, and the first parameter
as if it were the second. So now we've just swapped our function around, and instead of taking
an integer followed by a string, it takes a string followed by an integer. Everything still works
like we expected it to. So there's all kinds of interesting things we could do here. We can also,
since 'bind' actually is designed to swallow all of the extra arguments passed to it, we can do
something like this; and it still compiles, and we still get the same output. To me I definitely
wonder if this is a fallout of the fact that 'bind' was implemented before variadic templates,
and before C++11, and before 'auto', and all of these niceties; and then it makes me wonder
if this is actually just because it was easier to design it so that it swallows all the extra
arguments up to however many it is it can take - it says its unspecified I believe. I'm kind
of curious now if you would get an error after it swallows too many. It doesn't seem to care.
So anyhow... So now the next thing you need to know about 'bind' is that because it expects a
concrete function argument here. If this was a template, and we say we want this first thing
being passed in to be of type T, and now we try to compile, we're going to get an error here because
of an "unresolved overloaded function type" being passed as the first parameter to 'bind'. That's
because you cannot simply take a pointer to a template function. There's no way to do this.
It has to be a concrete thing. So we can specify 'int' and now it works as we expected again,
and we get the expected results. It's also worth noting that since 'bind' returns a callable thing,
it can be passed into std::function. So if we wanted to make something that could be passed
around with a known type, then we might want to do something like "void int" and we can
initialize it with the 'bind' result... (At least we should have been able to.)
"Cannot convert basic string to int." Oh. That's because we swapped the order of the parameters in
our example up there. So 'bind' can be used to make all kinds of nifty little adaptor things.
There we go. Indeed before 'auto' existed, you know, with some caveats passing the result of
a 'bind' to a std::function was pretty much the only reasonable thing that you could do with it.
Now granted std::bind didn't come into existence until C++11 and C++11 also introduced the 'auto'
key word; but 'bind' was originally part of Boost; and if you were using it back in those days then
you really couldn't store it in any reasonable way. You kind of had to pass it around and put it
into some type of eracing wrapper, which is what std::function gives us. Then with std::function
we can continue our "hello world" kind of test here and get the results that we expect still,
but it's now much more strongly typed than our 'bind' was because std::function requires all your
parameter types to match. So I guess that wraps it up. A couple things to keep in mind - 'print',
excuse me, 'bind', requires a function pointer or some sort of other callable thing,
so you can't pass the template to it that's just a generic template. You
can reorder arguments. Keep in mind that it copies the arguments that you pass in.
So I hope you enjoy this episode and be sure to leave a comment or share the video with
someone else. Be sure to subscribe, follow me on Twitter, and check out any of the links below.