As I understand it,
std::bind
perfectly forwards both the callable object it wraps and the arguments to that callable object;- the
std::bind
return object is itself movable and/or copyable, depending on whether the callable object and its arguments are movable and/or copyable; - a
std::bind
return object may be nested, in which case the outerstd::bind
return object is movable and/or copyable, just as when binding other callable objects.
Therefore, I would expect the following code snippet to compile okay. Instead, the code generates a spew of compiler errors at the last two statements in main()
.
#include <functional>
template<typename HandlerType>
void call_handler(HandlerType&& handler)
{
handler();
}
template<typename HandlerType>
void do_something(HandlerType&& handler)
{
auto f = std::bind(
&call_handler<HandlerType&>,
std::forward<HandlerType>(handler));
f();
}
int main()
{
auto a = [&]() {};
do_something(a);
do_something(std::move(a));
auto b = std::bind([&]() {});
do_something(b); // <- compiler error!
do_something(std::move(b)); // <- compiler error!
}
Each of the two problem line spews errors without the other. To eliminate all errors, I must comment out both lines.
Here's a sample error, from g++ 4.9.2 in Cygwin, at the call to f()
in do_something()
:
(4 of 103): error: no match for call to ‘(std::_Bind<void (*(std::_Bind<main()::<lambda()>()>))(std::_Bind<main()::<lambda()>()>&)>) ()’
Here's a sample error from Visual Studio 2013, at the same line:
1>C:\Program Files (x86)\Microsoft Visual Studio12.0\VC\include\functional(1149): error C2664: 'void (HandlerType)' : cannot convert argument 1 from 'void' to 'std::_Bind<false,void,main::<lambda_2b8ed726b4f655ffe5747e5b66152230>,> '
What's going on? Do I misunderstand std::bind
?
Specifically, how can I
- bind a callable object? and
- pass that
std::bind
return object to a function taking a universal reference? and - nest that
std::bind
return object in anotherstd::bind
?
My goal is for the underlying callable object and its arguments to be perfectly forwarded.
EDIT: To clarify, I want to pass the wrapped callable object and its arguments by value, not by reference, so std::ref
won't help—at least, not as a full solution. The reason is that my real code is more complex and involves passing the f
std::bind
return object across a thread boundary, and both the a
and b
std::bind
return objects may go out of scope in the original thread before call_handler
calls f()
, so a
and b
need to copy or move into f
, not be mere references. That said, my question is specifically about std::bind
and perfect forwarding, and for the purpose of asking a good question, I've distilled out everything not needed to reproduce the specific compiler errors I mentioned.
Your assumption 1 is wrong,
bind
always passes the bound arguments as lvalues to the callable it is wrapping. To demonstrate this, change thebind
expression withindo_something
to the followingThe following line will then fail to compile
because
decltype(handler)
is an rvalue reference, butbind
will try to callcall_handler
with an lvalue reference to the bound lambda expression you passed it inmain
.Now for what's going wrong in the second half of your example.
bind
has special handling for nestedbind
expressions, which it will recognize and evaluate. However, in your example, you don't want that to happen. Instead, you want the nestedbind
to be forwarded as is tocall_handler
, which will then invoke it.Boost provides
boost::protect
which lets you mask the real type of the nestedbind
and thus prevent its evaluation by the outerbind
.Unfortunately, there is no
std::protect
equivalent, but it's not difficult to write it yourself.Just wrap your inner
bind
expression withprotect
, and your code will compile.Live demo