Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between std::async and std::bind when wrapping rvalue reference lambda

Inspired by this comment about binding lambdas with rvalue reference parameters directly to std::async, binding an rvalue to a lambda through std::async compiles and executes as expected: (live example)

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

Using std::bind, however, triggers a compiler error: (live example)

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

This is because std::bind keeps message as an lvalue, so that when it passes it to the lambda, the argument no longer matches the parameter.

I've read that std::async internally uses std::bind, so how does it get away with rvalue reference parameters when std::bind does not? Is there a particular part of the standard that requires this behavior or is this dependent on the compiler?

like image 312
huu Avatar asked May 06 '15 19:05

huu


1 Answers

I've read that std::async internally uses std::bind, so how does it get away with rvalue reference parameters when std::bind does not?

It doesn't use bind internally. (Or rather, it couldn't without going through some epic contortions like the one in @Praetorian's answer in that question, and it's much easier to just write something separately).

It is usually implemented using a somewhat bind-like machinery, but it's much simpler because it doesn't have to handle all sorts of weird things bind handles (nested binds, placeholders, dropping extra arguments, etc.)

Is there a particular part of the standard that requires this behavior or is this dependent on the compiler?

It's required by the standard. The specification of bind is ridiculously dense, but does require plain bound arguments to be passed as lvalues ([func.bind.bind]/p10, bullet 4).

async is specified to call INVOKE (DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...), and DECAY_COPY always returns an rvalue.

like image 170
T.C. Avatar answered Oct 26 '22 08:10

T.C.