【问题标题】:No viable overloaded '=' for assigning std::function callback as member function没有可行的重载 '=' 用于将 std::function 回调分配为成员函数
【发布时间】:2016-02-08 16:36:46
【问题描述】:

我正在尝试将成员函数与std::function 回调挂钩。回调类型为:

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

我的成员函数是:

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

当我尝试使用以下方法分配回调时:

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

我收到多个候选人的No viable overloaded '=' 错误。以下是我认为最相关的一项的详细信息:

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`

我尝试了几种不同的 bind() 安排,但我不确定错误告诉我什么。就参数和返回值而言,这些类型似乎都是正确的,所以我不确定它为什么不接受它?

【问题讨论】:

  • 尝试添加两个std::placeholders
  • 你需要传递第二个参数
  • @LogicStuff @sunny1304 不过,我没有其他要绑定的了。这些参数将在回调中传递给 me。我只是将函数绑定到this,以便我可以使用成员函数作为回调。

标签: c++ c++11 callback cocos2d-x


【解决方案1】:

试试

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

或者试试 lambda 函数

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

【讨论】:

  • 嗯,第一个确实有效!我曾尝试仅使用占位符::_1 绑定,但不是第二个。绑定调用实际上在做什么?!
  • 哦,lambda 很好——我已经在使用它们,但想改用成员函数。不过,这是一个不错的选择。
  • 在对 bind() 进行了更多阅读之后,我想我明白了。两个占位符用于 Touch* 和 Event* 参数。出于某种原因,我认为占位符定义了我传递的参数的位置——this。这就是为什么我在尝试时只包含一个。但现在我想我明白发生了什么!
  • @aardvarkk 只是补充一下,这是一个漫长而神奇的事情:-) ...但我很高兴你现在明白了
  • @aardvarkk bind 只是简单地写了一些与 lambda 几乎相同的东西,同时如果你尝试递归地 bind 会增加一些额外的烦人的魔法。这种递归的极端情况导致我避免使用bind,因为它很少比简单的lambda更短或更清晰,而且它有机会咬你。
【解决方案2】:

bind 必须被告知如何处理传递给其返回值的参数。默认情况下,它只是丢弃它们。

当您将它传递给std::function 时,它会尝试键入擦除它。 std::functionTouch*, Event* 传递给 bindbind 的返回值会丢弃它们,并按照您的要求调用 (this-&gt;*&amp;Game::onTouchEnded)()。这不是一个有效的调用,你会得到一个错误。

解决此问题的简单方法是添加占位符,说明“如何处理绑定返回值的参数”。 bind 将成员函数视为首先接受额外 this 参数的函数。你想要:

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

对应于:

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

现在这一切都很好,但你真的应该避免std::bind。完全理解它是很棘手的:当你递归调用它时,它有奇怪的极端情况,可能会咬你,或者强迫你进行不必要的类型擦除。

改为使用 lambda。在 C++11 中它看起来像:

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

在 C++14 中,另一种选择是:

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

甚至:

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)...);
  };
}

可以这样使用:

bind_method(this, &Game::onTouchEnded)

并且是您致电bind 的直接替代品。在这里,我们将参数自动转发给我们调用的方法,而不必显式列出它们。

【讨论】:

  • 我实际上是在使用 lambda,但内联代码开始变得非常冗长和分散注意力。您会说使用仅将参数转发给成员函数的单行 lambda 优于直接调用 bind 吗?这就是我从您上面的其他评论中收集到的内容。
  • @aardvarkk 是的。任何 C++ 开发人员都应该至少了解基本的 lambda,但很少有 C++ 开发人员完全了解 bind。一个简短的 lambda 至少和 bind 一样清晰。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-10
  • 2015-06-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
相关资源
最近更新 更多