As I understand it,
std::bindperfectly forwards both the callable object it wraps and the arguments to that callable object;- the
std::bindreturn object is itself movable and/or copyable, depending on whether the callable object and its arguments are movable and/or copyable; - a
std::bindreturn object may be nested, in which case the outerstd::bindreturn 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::bindreturn object to a function taking a universal reference? and - nest that
std::bindreturn 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,
bindalways passes the bound arguments as lvalues to the callable it is wrapping. To demonstrate this, change thebindexpression withindo_somethingto the followingThe following line will then fail to compile
because
decltype(handler)is an rvalue reference, butbindwill try to callcall_handlerwith 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.
bindhas special handling for nestedbindexpressions, which it will recognize and evaluate. However, in your example, you don't want that to happen. Instead, you want the nestedbindto be forwarded as is tocall_handler, which will then invoke it.Boost provides
boost::protectwhich lets you mask the real type of the nestedbindand thus prevent its evaluation by the outerbind.Unfortunately, there is no
std::protectequivalent, but it's not difficult to write it yourself.Just wrap your inner
bindexpression withprotect, and your code will compile.Live demo