Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to pass an object created by std::bind to a function?

Let's assume I want to pass a function object created by std::bind by reference to a funktion:

void myCallback(int i, int j)
{
    std::cout << "toCall , i=" << i << " j=" << j;
}

void worker(std::function<void(int)> & callback)
{
    callback(111);
}

int main(int argc, char** argv)
{
    auto foo = std::bind(myCallback, std::placeholders::_1, 222);
    worker(foo);
}

This does not compile

compiler error

Severity Code Description Project File Line Suppression State Error C2664 'void worker(std::function &)': cannot convert argument 1 from 'std::_Binder &,int>' to 'std::function &' asn1test_1 D:.....\asn1test_1.....cpp 302

However, passing by value works:

void worker(std::function<void(int)> callback)
{
    callback(111);
}

When I avoid "auto" and use instead

std::function<void(int)> foo = std::bind(myCallback, std::placeholders::_1, 222);

it works both with passing by reference or by value.

Q1: Why this behavior?

Q2: What would be the "right" way or datatype to pass an object created by std::bind to a function?

like image 439
MichaelW Avatar asked Jan 28 '20 21:01

MichaelW


People also ask

What does std :: bind do in C++?

std::bind is a Standard Function Objects that acts as a Functional Adaptor i.e. it takes a function as input and returns a new function Object as an output with with one or more of the arguments of passed function bound or rearranged.

How does STD bind work?

std::bind allows you to create a std::function object that acts as a wrapper for the target function (or Callable object). std::bind also allows you to keep specific arguments at fixed values while leaving other arguments variable.

What is boost bind?

boost::bind is a generalization of the standard functions std::bind1st and std::bind2nd. It supports arbitrary function objects, functions, function pointers, and member function pointers, and is able to bind any argument to a specific value or route input arguments into arbitrary positions.


Video Answer


3 Answers

std::bind does not return a std::function, at least, it is not required to do so. When you do

auto foo = std::bind(myCallback, std::placeholders::_1, 222);
worker(foo);

foo is not a std::function and worker needs a non-const reference to a std::function, so the compiler errors out. If you switch worker to being

void worker(const std::function<void(int)> & callback)

then you'll have a const reference and that can be bound to the temporary object that the conversion of foo to a std::function would produce.


I'd also like to point out that since we have lambdas, especially generic ones, std::bind really isn't needed. Keeping the change to worker, you could instead make foo

auto foo = [](auto var){ return myCallback(var, 222); };
like image 182
NathanOliver Avatar answered Oct 18 '22 00:10

NathanOliver


std::bind() does not return a std::function, but an implementation-defined type that is convertible to a std::function.

When worker() takes its callback parameter as a non-const reference, that prevents the compiler from performing any implicit conversions, as a non-const reference cannot be bound to a temporary object. The reference will require an actual std::function object to have been created explicitly beforehand, eg:

std::function<void(int)> foo = std::bind(myCallback, std::placeholders::_1, 222);
worker(foo);

Changing worker() to take its callback parameter by value, or by const reference, allows the compiler to perform an implicit conversion for you, so you can pass the result of std::bind() as-is and the compiler will create a temp std::function for you.

like image 5
Remy Lebeau Avatar answered Oct 18 '22 00:10

Remy Lebeau


If you want to avoid the overhead of converting to a std::function, you could use a template:

template<class CB> void worker(CB callback) {
    callback(111);
}
like image 2
Chris Dodd Avatar answered Oct 18 '22 00:10

Chris Dodd