22

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.
Sufian
  • 6,405
  • 16
  • 66
  • 120
user3724853
  • 547
  • 3
  • 10
  • 2
    Please post the full error message, with notes and all. – n. m. could be an AI Jun 10 '14 at 07:08
  • Interestingly enough, the first line doesn't seem to compile if you use clang with `-stdlib=libc++`. – T.C. Jun 10 '14 at 07:32
  • The problem is with your rvalue reference, [here's a tersely answered question on how to get that to work](http://stackoverflow.com/questions/5126219/is-there-a-reference-wrapper-for-rvalue-references). –  Jun 10 '14 at 07:44
  • BTW, for questions like this, you get a more useful diagnostic if you attempt to invoke the result of `std::bind` directly. –  Jun 10 '14 at 07:45

2 Answers2

17

INTRODUCTION

std::bind will try to call func1<Handler> with an lvalue-reference, but your instantiation of func1 will make it only accept rvalues.


EXPLANATION

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
}
Community
  • 1
  • 1
Filip Roséen - refp
  • 62,493
  • 20
  • 150
  • 196
  • Okay, thanks. So there is no way to move variable(s) by using **std::bind**? The only way is C++1y generic lambda (which cannot use in my production code, sadly). – user3724853 Jun 10 '14 at 08:00
  • 3
    @user3724853 you could create a wrapper that would yield an rvalue-reference through a *conversion-function*, but one has to be **very** careful if the resulting *function-object* will be called more than once (since you have effectively already moved from the wrapped object). – Filip Roséen - refp Jun 10 '14 at 08:02
  • This makes me wonder if one-shot lambda syntax might be useful -- `&&` qualified `operator()` only. A `&&` qualified overload for all lambdas that treat class-locals like locals. And a way to extract the r-l value of the context a lambda was called in to conditionally forward data. – Yakk - Adam Nevraumont Jun 15 '14 at 02:31
1

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

Andrei R.
  • 2,374
  • 1
  • 13
  • 27