aardvarkk February 2016

No viable overloaded '=' for assigning std::function callback as member function

I'm trying to hook a member function up to an std::function callback. The callback type is:

std::function<void(Touch*, Event*)>

My member function is:

void Game::onTouchEnded(Touch* touch, Event* event)

When I try to assign the callback using the following:

listener->onTouchEnded = bind(&Game::onTouchEnded, this);

I get a No viable overloaded '=' error with several candidates. Here are the details of the one I believe to be most relevant:

Candidate function not viable: no known conversion from 
'__bind<void (Game::*)(cocos2d::Touch *, cocos2d::Event *), Game *>'
to
'std::__1::function<void (cocos2d::Touch *, cocos2d::Event *)>'
for 1st argument`

I've tried several different bind() arrangements but I'm not sure what the error is telling me. The types all seem to be correct in terms of the parameters and return value so I'm not sure why it won't accept it?

Answers


WhiZTiM February 2016

Try

listener->onTouchEnded = bind(&Game::onTouchEnded, this, std::placeholders::_1, std::placeholders::_2);

or try a lambda function

listener->onTouchEnded = [this](Touch* touch, Event* event){ this->onTouchEnded(touch, event); };


Yakk February 2016

bind must be told what to do with parameters passed to its return value. By default, it simply discards them.

When you pass it to the std::function, it tries to type-erase it. std::function passes a Touch*, Event* to the bind. bind's return value discards them, and calls (this->*&Game::onTouchEnded)(), as you requested. This is not a valid call, and you get an error.

The simple way to fix this is to add placeholders that say "what do do with arguments to the bind return value". bind treats member functions as functions that take an extra this argument first. You want:

(this->*&Game::onTouchEnded)(_1, _2)

which corresponds to:

std::bind(&Game::onTouchEnded, this, _1, _2)

Now this is all well and good, but really you should avoid std::bind. Fully understanding it is tricky: It has strange corner cases that can bite you, or force you to needlessly type-erase, when you call it recursively.

Instead, use a lambda. In C++11 it looks like:

[this](Touch* touch, Event* event){return this->onTouchEnded(touch, event);}

In C++14, an alternative is:

[this](auto&&args)->decltype(auto){return this->onTouchEnded(decltype(args)(args)...);}

or even:

template<class T, class M>
auto bind_method( T* t, M* m ) {
  return [t, m](auto&&...args)->decltype(auto){
    return (t->*m)(decltype(args)(args)...);
  };
}

which can be used like this:

bind_method(this, &Game::onTouchEnded)

and is a drop-in replacement to your call to bind. Here, we auto-forward the arguments to the method we call, instead of having to list them explicitly.

Post Status

Asked in February 2016
Viewed 1,804 times
Voted 12
Answered 2 times

Search




Leave an answer