Passing References to Deferred Function Calls with std::ref

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <thread>
#include <iostream>
void start_thread(int& param) {
std::cout << param << std::endl;
}
int main() {
int value(7);
std::thread mythread(start_thread, value);
mythread.join();
return 0;
}

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:

1
2
3
4
5
6
7
8
9
10
11
In file included from /usr/include/c++/5.3.0/thread:39:0,
from main.cpp:1:
/usr/include/c++/5.3.0/functional: In instantiation of ‘struct std::_Bind_simple<void (*(int))(int&)>’:
/usr/include/c++/5.3.0/thread:137:59: required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(int&); _Args = {int&}]’
main.cpp:11:45: required from here
/usr/include/c++/5.3.0/functional:1505:61: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
typedef typename result_of<_Callable(_Args...)>::type result_type;
^
/usr/include/c++/5.3.0/functional:1526:9: error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’
_M_invoke(_Index_tuple<_Indices...>)
^

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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* @brief Primary class template for reference_wrapper.
* @ingroup functors
* @{
*/
template<typename _Tp>
class reference_wrapper
: public _Reference_wrapper_base<typename remove_cv<_Tp>::type>
{
// If _Tp is a function type, we can't form result_of<_Tp(...)>,
// so turn it into a function pointer type.
typedef typename _Function_to_function_pointer<_Tp>::type
_M_func_type;
_Tp* _M_data;
public:
typedef _Tp type;
reference_wrapper(_Tp& __indata)
: _M_data(std::__addressof(__indata))
{ }
reference_wrapper(_Tp&&) = delete;
reference_wrapper(const reference_wrapper<_Tp>& __inref):
_M_data(__inref._M_data)
{ }
reference_wrapper&
operator=(const reference_wrapper<_Tp>& __inref)
{
_M_data = __inref._M_data;
return *this;
}
operator _Tp&() const
{ return this->get(); }
_Tp&
get() const
{ return *_M_data; }
template<typename... _Args>
typename result_of<_M_func_type(_Args...)>::type
operator()(_Args&&... __args) const
{
return __invoke(get(), std::forward<_Args>(__args)...);
}
};

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <thread>
#include <iostream>
#include <functional>
void start_thread(int& param) {
std::cout << param << std::endl;
}
int main() {
int value(7);
std::thread mythread(start_thread, std::ref(value));
mythread.join();
return 0;
}

Output:

1
7

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <functional>
void print_value(int& param) {
std::cout << param << std::endl;
}
int main() {
int value(7);
auto f = std::bind(print_value, value);
f();
return 0;
}

Here’s the key section of the assembly output, for the main function:

1
2
3
4
5
6
7
8
9
main:
subq $24, %rsp
leaq 8(%rsp), %rdi
movq print_value(int&), (%rsp)
movl $7, 8(%rsp)
call print_value(int&)
xorl %eax, %eax
addq $24, %rsp
ret

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):

1
2
3
4
5
6
7
8
main:
subq $24, %rsp
leaq 12(%rsp), %rdi
movl $7, 12(%rsp)
call print_value(int&)
xorl %eax, %eax
addq $24, %rsp
ret

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.

Share