如何创建一个填充了 C 风格函数指针和 lambda 的向量(有和没有捕获)

Posted

技术标签:

【中文标题】如何创建一个填充了 C 风格函数指针和 lambda 的向量(有和没有捕获)【英文标题】:How to create a vector filled with C-Style function pointers and lambdas(with and without captures) 【发布时间】:2019-10-16 19:11:27 【问题描述】:

我最近一直在学习 lambda 和函数指针,并想在一个简单的回调系统中使用它们。 存储事件和触发时应调用的所有回调的映射如下所示:std::unordered_map< sf::Event::EventType , std::vector< Callback > > eventMap;

我是这样定义回调的 typedef void(*Callback)(sf::Event const&);。我的registerEvent() 函数接受一个事件和一个回调作为参数。registerEvent() 然后可以这样调用:

inputHandler.registerEvent(/*Any event type*/, [](sf::Event const&)

    //do something
);

这很好,但是当我想捕获一个对象时,我得到一个编译错误说 error: no matching function for call to ‘InputHandler::registerEvent(sf::Event::EventType, Button::Button(ID, Callback, InputHandler&)::<lambda(const sf::Event&)>)’ 产生该错误的代码如下:

class Button

//
public:
    Button(ID id, Callback callback, InputHandler& inputhandler)
    
        inputhandler.registerEvent(sf::Event::MouseButtonPressed, [this](sf::Event const&)
        
            
              getTextureRect();  
            
        );
    
;

似乎this 捕获正在更改 lambda 的类型,这使其与 registerEvent() 函数不兼容。但是由于许多不同的类应该能够创建这样的 lambda,并且向量中应该包含没有任何捕获的 lambda,所以我不知道如何判断向量应该是什么类型。我听说过std::function,但是(这听起来可能很傻)我想使用 C 风格的方式来更好地理解。我认为this 帖子对此有所说明,但我不太了解它。我考虑过创建两张地图,一张用于所有无捕获的 lambda,一张用于其他。然后我可以让每个类都从基类继承,并使向量中的捕获类型成为指向基类的指针,尽管这非常复杂并且增加了不必要的开销,我什至不知道这是否可行。 对于这种情况,一般的解决方案是什么?

感谢所有回复和问候,

丹尼尔·施赖伯·门德斯 :)

编辑: 回调现在看起来像这样: typedef std::function< void(sf::Event const&) > Callback; 当我想使用它时:

//inside the Button Constructor
inputhandler.registerEvent(sf::Event::MouseButtonPressed, Callback([this](sf::Event const& event)
        
            //memberFunc();
        ));

这并没有给我一个错误,而是以错误的方式表现。它确实执行了 lambda,但没有更改定义它的类。所以我在构造函数中添加了一行,它只打印刚刚创建的对象的地址。我只创建一个按钮,所以应该只打印一个地址。但有两种不同。这意味着当将包装在 std::function 中的 lambda 插入向量时,会创建一个新的 Button 对象。怎么可能?隐式创建对象?

感谢您的回答:)

【问题讨论】:

Simliar issue. 【参考方案1】:

带有捕获的 Lambda 不能转换为函数指针。普通函数指针没有任何地方可以存储 lambda 捕获的状态。

std::function 是一个对象,因此可以存储带有或不带有捕获的 lambda。

如果必须使用原始函数指针,则需要存储函数指针和状态,然后在调用时将状态传递给函数。例如

typedef void(*Callback)(sf::Event const&, void* data);
std::unordered_map< sf::Event::EventType , std::vector< std::pair< Callback, void* > > > eventMap;

然后您需要将您的状态存储在一个对象中,将该对象存储在某个位置以使其保持活动状态。在您的对象内部,您需要从 void* 转换回您的状态对象指针。如果您不使用需要普通函数指针的 c 库,那么使用 std::function 为您完成所有这些操作确实要简单得多。

【讨论】:

哦,好的,谢谢。使用 std::function 的解决方案大致是什么样的? 只需将您的 typedef 更改为 typedef std::function&lt;void(sf::Event const&amp;)&gt; Callback;std::function 几乎就像一个普通的函数指针,除了您可以为它们分配任何类型的仿函数(包括 lambda) 谢谢艾伦;关于“然后你需要将你的状态存储在一个对象中”:你的意思是例如this 作为一个州?那么void* data 是调用 lambda 的对象的参数吗? 您可以根据需要在函数中使用它。坚持std::function,除非你真的需要做一些不同的事情 你应该问一个新问题(如果你愿意,可以链接回这个问题)而不是在你现有的问题中编辑一个新问题

以上是关于如何创建一个填充了 C 风格函数指针和 lambda 的向量(有和没有捕获)的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 使用(1)仿函数,(2)函数指针和(3)c风格qsort来分析排序速度

如何从 char* 函数返回指针

c ++如何创建一个即使在对象被覆盖时也会持续的指针?

C++ 指针和 C 风格的数组初始化

内存分配(指针和堆栈)

c ++使用指针访问对象的成员