C++11 introduced a ton of new features to ISO C++, admittedly already a very large language. Using C++ can often be surprising for many different reasons, but the most pleasant of surprises when using C++ is discovering a useful feature added in one of the recent specifications, or finding a (possibly additional) use for a feature you already knew about. I recently had such an experience with std::ref
.
At first glance, it doesn’t seem all that useful to have a “reference wrapper” as opposed to a plain old reference. Its immediate usefulness is obscured by the fact that it is only really useful when using other new features added with C++11 like std::bind
and std::thread
. std::ref
is a workaround for a case where template type deduction rules deduce a parameter type such that the parameter is taken by value, when the intention was to take the parameter by reference. Consider the following example, loosely based on the code I was working on:
|
|
Ignore the obvious design flaws here; this example is only meant to demonstrate what happens when you try and pass a value parameter by reference to a function being called from a templated function like std::thread::thread()
. Note that template type deduction is (correctly) deducing the type of value to be int
, but this doesn’t match the type int&
that it sees in start_thread. The actual error given by the compiler (in this case GCC) is actually much more obscure than that:
|
|
The compiler does show that the error is vaguely to do with binding the function parameters to the function passed in; a quick search online using keywords from the error pointed towards std::ref
as the solution.
So how does std::ref
solve the problem of passing parameters by reference into templated functions that have their parameter types deduced by value? The definition of std::reference_wrapper
(the type returned by std::ref
) and its libstdc++ documentation/source paint a pretty clear picture. std::reference_wrapper
is just a thin wrapper around a pointer that implicitly converts to a reference (through operator T&() const
):
|
|
So the deduced type of the parameter in the template function is std::reference_wrapper<T>
(std::reference_wrapper<int>
in the case of the example above), which can be passed into the function by value. This is the same size as a reference, and implicitly casts to one when it is passed to another function that expects one. So functionally, it smells and acts much like a reference. Let’s see it in action:
|
|
Output:
|
|
Great, no more messy template error messages, and everything works as expected.
To demonstrate the low overhead of this approach, I’ll use a slightly simpler example which uses std::bind
instead of std::thread
.
|
|
Here’s the key section of the assembly output, for the main
function:
|
|
With the optimization level set at -O3
the assembly output is pretty tight. Compare that with the assembly after I remove the bind call and just call print_value
directly (the rest of the assembly is identical):
|
|
I’m no assembly expert, but I believe the difference is a function pointer referring to the print_value function and a result of the indirect function call (via std::bind
). It certainly seems to be true that std::ref
imposes no additional overhead to the function call by itself.
There’s also std::cref
which I haven’t explicitly shown, but works in exactly the same way as std::ref
for const&
parameter types. If anything, I’ve used this more than the canonical std::ref
function. If you’re using C++03, you can still access this functionality via boost::ref
(and boost::thread, boost::bind
, etc.).
So there you have it! std::ref
and std::cref
have both become valuable tools in my C++11 and C++14 toolbox. Next time you reach for std::bind
, std::thread
or another C++ utility that performs a deferred function call internally, consider whether you should make use of std::ref
or std::cref
.