8

I'm trying to bind an rvalue reference to a lambda using std::bind, but I have issues when I throw that into a std::async call: (source)

auto lambda = [] (std::string&& message) {
    std::cout << message << std::endl;
};
auto bound = std::bind(lambda, std::string{"hello world"});
auto future = std::async(bound); // Compiler error here
future.get()

This issues a compiler error I'm not really sure how to interpret:

error: no type named 'type' in 'class std::result_of(std::basic_string)>&()>'

What's going on here? Interestingly, a slight modification does compile and work as expected. If I change std::string{"hello world"} to a c-string literal, everything works fine: (source)

auto lambda = [] (std::string&& message) {
    std::cout << message << std::endl;
};
auto bound = std::bind(lambda, "hello world");
auto future = std::async(bound);
future.get(); // Prints "hello world" as expected

Why does this work but not the first example?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
huu
  • 7,032
  • 2
  • 34
  • 49
  • It also works, if the lambda is defined with `auto lambda = [] (std::string& message)` (reference, not rvalue) – user1978011 May 06 '15 at 18:15
  • I've noticed that as well, though it should be `const string& message`, otherwise it'll refuse to accept a c-string literal. – huu May 06 '15 at 18:17
  • 2
    Just let `async` do the binding for you: `std::async(lambda, std::string{"hello world"})`. – T.C. May 06 '15 at 18:25

1 Answers1

11

std::bind is going to make a copy of the std::string argument and pass that to the lambda. But that fails to compile because the lambda requires an rvalue argument, while what bind passes it will be an lvalue. You could get this to work if you get bind to move the argument, but this requires extremely ugly casting for disambiguation (because std::move is an overloaded function).

auto bound = std::bind(lambda, std::bind(static_cast<std::string&&(*)(std::string&)>(std::move),
                                         std::string{"hello world"}));

Live demo

You could, of course, write your own version of move that is not overloaded, and avoid that cast.

The second case works because when bind passes the char const * to the lambda, an rvalue std::string temporary is created implicitly.


To explain the error message you're seeing, somewhere within the innards of std::async, std::result_of is being invoked to determine the return type of the function call expression. However, because that call expression is invalid due to the reasons explained above, result_of is being SFINAE'd out (this is a C++14 change). Hence the error error: no type named 'type' in 'class std::result_of<...>'.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • 4
    Ugh. Yes, that's ugly as hell. – T.C. May 06 '15 at 18:24
  • Very clever. Makes sense since `lambda("hello world")` would compile but `auto s = std::string{"hello world"}; lambda(s)` would not. – huu May 06 '15 at 18:32
  • 1
    Another option in this case is to just create a new lambda. `auto bound = [lambda,msg=std::string{"hello world"}]() mutable { lambda(std::move(msg)); };` – Brian Bi May 06 '15 at 18:39
  • @Brian, you're right, a generalized lambda capture would obviate the need for `std::bind`. I'm restricted to C++11 at the moment though, so that won't work in my situation, but that's good to know in case C++14 does become an option. – huu May 07 '15 at 01:57