The following code does not compile:
#include <functional>
template<class ...Args>
void invoke(Args&&... args)
{
}
template<class ...Args>
void bind_and_forward(Args&&... args)
{
auto binder = std::bind(&invoke<Args...>, std::forward<Args>(args)...);
binder();
}
int main()
{
int a = 1;
bind_and_forward(a, 2);
}
If I understand correctly, the reason is as follows: std::bind
copies its arguments, and when the binder
's operator()
is called, it passes all the bound arguments as lvalues - even those ones that entered bind
as rvalues. But invoke
was instantiated for the original arguments, and it can't accept what the binder
attempts to pass it.
Is there any solution for this problem?
Your understanding is correct -
bind
copies its arguments. So you have to provide the correct overload ofinvoke()
that would be called on the lvalues:This works on most types. There are a few exceptions enumerated in [func.bind.bind] for
operator()
, whereArg&
is insufficient. One such, as you point out, isstd::reference_wrapper<T>
. We can get around that by replacing theArgs&
usage above with a type trait. Typically, we'd just add an lvalue reference, but forreference_wrapper<T>
, we just wantT&
:Plug that back into the original solution, and we get something that works for
reference_wrapper
too:Of course, if one of
Arg
is a placeholder this won't work anyway. And if it's a bind expression, you'll have to write something else too.