It\'s pretty well known that the default behaviour of std::bind and std::thread is that it will copy (or move) the arguments passed to it, and to use reference semantics we will
I did in fact write a small utility that creates a delayed invocation functor (somewhat std::bind
-like, but without the nested bind expressions/placeholders features). My main motivation was this case that I found counter-intuitive:
using pointer_type = std::unique_ptr;
pointer_type source();
void sink(pointer_type p);
pointer_type p = source();
// Either not valid now or later when calling bound()
// auto bound = std::bind(sink, std::move(p));
auto bound = std::bind(
[](pointer_type& p) { sink(std::move(p)); }
, std::move(p) );
bound();
The reason for that adaptor (which moves its lvalue ref argument to sink
) is that the call wrapper return by std::bind
always forwards the bound arguments as lvalues. This wasn't a problem with e.g. boost::bind
in C++03 since that lvalue would either bind to a reference argument of the underlying Callable object or to a value argument via a copy. Doesn't work here since pointer_type
is move-only.
The insight that I got is that there really are two things to consider: how the bound arguments should be stored, and how they should be restored (i.e. passed to the Callable object). The control that std::bind
grants you is as follows: arguments are either stored in a shallow (via the use of std::ref
) or regular manner (using std::decay
with perfect forward); they are always restored as lvalues (with cv-qualifiers inherited from the owning call wrapper). Except that you can bypass the latter with a small on-site adaptor lambda expression like I just did.
It's arguably a lot of control and a lot of expression for relatively little to learn. In comparison my utility has semantics like bind(f, p)
(decay and store copy, restore as lvalue), bind(f, ref(p))
(store shallowly, restore as lvalue), bind(f, std::move(p))
(decay and store from move, restore as rvalue), bind(f, emplace(p))
(decay and store from move, restore as lvalue). This feels like learning an EDSL.