Reputation: 567
I am trying to store a forward function into std::function
. If I use std::bind
, I get error message like no viable conversion from ...
. If I use lambda, it compile okay.
Here is sample code
#include <functional>
template<typename Handler>void func1(int a, Handler&& handler) {}
template<typename Handler>void func2(Handler&& handler)
{
// this line compile fine
std::function<void ()> funcA = [handler = std::move(handler)]() { func1(1, std::move(handler)); };
// this line got compile error
std::function<void ()> funcB = std::bind(func1<Handler>, 1, std::move(handler));
}
int main()
{
func2(&main); // this just a sample, I am using functor as argument in real code
}
Trying both g++ --std=c++1y (v4.9.0) and clang++ --std=c++1y (v3.4.1) yield the same result
edit: clang++ error message
main.cpp:8:28: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int
(*&&)()), int, int (*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to
'std::function<void ()>'
std::function<void ()> funcB = std::bind(&func1<Handler>, 1, std::move(handler));
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:5: note: in instantiation of function template specialization 'func2<int (*)()>' requested here
func2(&main);
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2181:7: note: candidate constructor not viable: no
known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
(*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'nullptr_t' for 1st argument
function(nullptr_t) noexcept
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2192:7: note: candidate constructor not viable: no
known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
(*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'const std::function<void ()> &'
for 1st argument
function(const function& __x);
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2201:7: note: candidate constructor not viable: no
known conversion from 'typename _Bind_helper<__is_socketlike<void (*)(int, int (*&&)())>::value, void (*)(int, int (*&&)()), int, int
(*)()>::type' (aka '_Bind<__func_type (typename decay<int>::type, typename decay<int (*)()>::type)>') to 'std::function<void ()> &&' for
1st argument
function(function&& __x) : _Function_base()
^
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.0/../../../../include/c++/4.9.0/functional:2226:2: note: candidate template ignored:
substitution failure [with _Functor = std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>]: no matching function for call to object of
type 'std::_Bind<void (*(int, int (*)()))(int, int (*&&)())>'
function(_Functor);
^
1 error generated.
Upvotes: 23
Views: 6971
Reputation: 2462
in short: function
has to be copyable. bind
with rvalue returns non-copyable object. Workaround is to capture/bind with shared_ptr
containing abovementioned value
Upvotes: 2
Reputation: 63807
std::bind will try to call func1<Handler>
with an lvalue-reference, but your instantiation of func1 will make it only accept rvalues.
Here we have reduced your testcase to the bare minimum to show what is going on, the snippet below is ill-formed and an explanation will follow to why that is.
#include <functional>
template<class T>
void foobar (T&& val);
int main() {
std::function<void()> f = std::bind (&foobar<int>, std::move (123));
}
In the above we will instantiate foobar
with T = int
, which makes the type of argument val to be an rvalue-reference to int (int&&
).
std::move(123)
will move-construct our value to be stored inside the object created by std::bind, but the Standard says that when std::bind later invokes the stored function, all arguments are passed as TiD cv &
; ie. as lvalues.
This behavior is mandated by the Standard (n3797), as stated in section [func.bind.bind]p10
.
By changing the previous ill-formed snippet into the following, no error will be raised, since foobar<int>
now accepts an lvalue-reference; suitable to be bound to the lvalue passed to our function by the function-object returned by std::bind.
std::function<void()> f = std::bind (&foobar<int&>, std::move (123));
#include <functional>
#include <type_traits>
#include <iostream>
int main() {
auto is_lvalue = [](auto&& x) {
return std::is_lvalue_reference<decltype(x)> { };
};
auto check = std::bind (is_lvalue, std::move (123));
bool res = check (); // res = true
}
Upvotes: 19