std::function与Lambda:传递成员函数的选择

4
我正在为游戏引擎创建一个消息系统,其中引擎的最终用户可以创建一个消息对象并将其传递给游戏对象以被监听器对象解释,这些监听器对象包含在与游戏对象附加的组件中。如果消息与监听器正在监听的消息匹配,则监听器应调用函数指针并将接收到的消息传递给它。基本结构看起来像这样:
class Message
{
    std::string message;
};

class Listener
{
    std::string target;
    void (*fn)(Message*);
};

使用游戏对象的代码来接收类似于以下内容的消息:
//if the queue is empty then dont do anything
if (messageQueue.empty())
{
    return;
}

//else grab the front of the queue
Message* newMessage = messageQueue.front();

//pop our message from the queue
messageQueue.pop();

//for each component in our list
for (std::vector<Component*>::iterator i = ComponentList.begin(); i < ComponentList.end(); i++)
{
    Component* itemp = *i;
    //for each message in its listener list
    for (std::vector<Listener*>::iterator j = itemp->Listeners.begin(); j < itemp->Listeners.end(); j++)
    {
        Listener* jtemp = *j;
        //check the listener against the newMessage
        if (jtemp->name == newMessage->name)
        {
            //call jtemp's function
            jtemp->function(newMessage);
        }
    }
}

我遇到的问题是,由于需要调用上面代码片段中的函数是一个组件类型的成员函数,所以我不能将`Listener.fn`设置为`Component::foo`或任何其他函数。
我向同学们咨询了一下,得到的两个建议是使用Lambda表达式或使用`std::function`将成员函数指针转换为常规函数指针。
我以前从未使用过Lambda表达式,但在查阅这里时,如果我理解正确,似乎每次传递消息都必须编写新的Lambda表达式,这与使用此类系统的目的相矛盾。在查阅这里这里这里有关`std::function`的资料后,似乎`std::function`将给我提供所需的灵活性,但其实现却给我带来了大量错误。
我是否忽视了Lambda的威力,我应该在这种情况下使用`std::function`,如果是,如何创建一个返回void并接受消息指针的`std::function`?

你似乎已经找到了回答自己问题所需的所有资源。我建议你尝试使用它们,如果你仍然无法解决问题,请针对你遇到的错误提出具体问题向我们寻求帮助。 - Barry
无论你使用什么,都不可能将成员函数指针转换为普通函数指针。C++没有提供任何标准功能来进行这种转换。无论是std::function还是lambda都无法帮助你实现这一点。你可以改用std::function来替代函数指针,但这意味着你必须彻底放弃"普通函数指针"的使用。 - AnT stands with Russia
1
请使用基于范围的for循环:omg。 - jterm
2个回答

6

std::function是存储的正确解决方案。 std::function几乎可以存储与其签名兼容的任何内容。

通常您将使用lambda填充它。 因为这比bind或其他替代方法更容易和清晰。

std::function<void(Message*)> fn;

fn = [comp](Message * m){ comp->foo(m); };

0

从我的经验来看,如果您可以访问C++11,请不要考虑'传统'函数指针。在C++中编写成员函数指针...很复杂。语法不直观,可能会导致脱发 :/

正如Yakk所说,std::function几乎可以在任何情况下使用。Lambda表达式虽然语法上有些棘手且微妙(lambda capture),但非常有用。将它们与std::function结合使用,您可能可以实现您想要的任何功能 x)

如果您仍然想使用传统的函数指针,我建议您阅读此线程(以及此网站一般为围绕C++的其他目的):
https://isocpp.org/wiki/faq/pointers-to-members#fnptr-vs-memfnptr-types


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接