5

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?

MichaelW
  • 1,328
  • 1
  • 15
  • 32

4 Answers4

5

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); };
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
5

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.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Why the compiler cannot make a conversion when there is a non-const-reference? – MichaelW Jan 28 '20 at 21:41
  • Even if it *did* return a std::function, you could not then pass a reference to that (unnamed, temporary) value as a non-const lvalue reference. The fact that a conversion is also involved doesn't make any difference. – Chris Dodd Jan 28 '20 at 21:51
  • 1
    @michael because a non-const reference cannot be bound to a temporary object. A const reference can – Remy Lebeau Jan 28 '20 at 21:53
  • 2
    @ChrisDodd If `bind` did return a `std::function` then the code would work. The OP is passing `foo` which would be an lvalue `std::function`. – NathanOliver Jan 28 '20 at 21:55
  • 1
    I was (mis)reading the code to directly pass the `std::bind` as an argument to `worker`, with no `auto foo` involved. Was the question edited? – Chris Dodd Jan 28 '20 at 22:00
2

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);
}
Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
1

It should be:

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

because then implicit conversion from foo to temporary std::function object can be made.

NutCracker
  • 11,485
  • 4
  • 44
  • 68
  • 2
    really the issue is that you can't pass a reference to an unnamed temp as a non-const lvalue reference. – Chris Dodd Jan 28 '20 at 21:52
  • 1
    I was (mis)reading the code to directly pass the `std::bind` as an argument to `worker`. – Chris Dodd Jan 28 '20 at 22:01
  • I got that, that's why I deleted my comment to your first comment. @ChrisDodd if you downvoted because of this reason, could you remove the downvote now – NutCracker Jan 28 '20 at 22:04