我目前正在处理一个小型库并遇到错误。我基本上要做的是有这样的语法:
Client client("", true);
client.on("ready", [](User object) {
object.test();
});
client.run();
当我打电话的时候
_eventManager->emit("ready", new User())
client.on();
方法假设用我传入的给定对象触发。我传递的对象可以是各种类。
我设计它的方式是为了使用一个名为“Object”的基类,我想要传递给emit
的所有类都来自Object
。但是,在尝试执行此操作时出现错误:
error C2664: 'void Discord::Client::on(const std::string &,const std::function<void (Discord::Object)> &)': cannot convert argument 2 from 'main::<lambda_54cb69ba8322d42f33e22a0f4f4926a4>' to 'const std::function<void (Discord::Object)> &'
2> z:\projects\visual studio 2015\discord\test\source\example1.cpp(12): note: Reason: cannot convert from 'main::<lambda_54cb69ba8322d42f33e22a0f4f4926a4>' to 'const std::function<void (Discord::Object)>'
2> z:\projects\visual studio 2015\discord\test\source\example1.cpp(12): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
我对自己做错了什么一无所知。 代码:
struct Event {
std::string name;
std::function<void(Object object)> function;
};
void Client::on(const std::string& name, const std::function<void(Object object)>& function) {
_eventManager->add(name, function);
}
void Client::run() {
User object;
_eventManager->emit("ready", object);
}
void EventManager::add(const std::string& name, const std::function<void(Object object)>& function) {
Event event;
event.name = name;
event.function = function;
_events.push_back(event);
}
void EventManager::emit(const std::string& name, const Object& object) {
for (unsigned int i = 0; i < _events.size(); i++) {
Event& event = _events[i];
if (event.name == name) {
event.function(object);
}
}
}
Error 先感谢您。
答案 0 :(得分:4)
您的函数声明为Object
对象:
std::function<void(Object object)> function;
但你传递的lambda需要User
:
client.on("ready", [](User object) {
object.test();
});
这是你在找什么?
client.on("ready", [](Object object) {
auto& user = static_cast<User&>(object); // not safe
user.test();
});
请注意,如果您尝试访问属于User
类但不属于Object
类的任何字段(可能是未定义的行为),这可能会导致应用程序崩溃,因为传递值“slice”数据。你应该做的是传递指针/参考:
client.on("ready", [](Object& object) { // <-- by ref here
auto& user = static_cast<User&>(object); // safer, at least won't crash when you pass User object
user.test();
});
请注意,这需要一些重大的重构。不仅在任何地方添加&
,而且您现在必须担心这些对象的生命周期。
答案 1 :(得分:1)
还可以尝试类似下面的内容。我制作了参数指针以防止切片,但问题的实际解决方案是createFunc
函数将传入的lambda绑定到调用传递的内部函数在lambda。它确保传入的类派生自Object
。
#include <iostream>
#include <functional>
#include <vector>
class Object;
struct Event {
std::string name;
std::function<void(Object* object)> function;
};
class Client
{
private:
std::vector<Event> events;
public:
void on(const std::string& name, const std::function<void(Object* object)>& function)
{
this->events.push_back(Event{name, function});
}
void run()
{
for (Event &e : events)
{
e.function(nullptr);
}
}
};
class Object
{
};
class User : public Object
{
};
template<typename T>
typename std::enable_if<std::is_base_of<Object, T>::value, std::function<void(Object*)>>::type
createFunc(std::function<void(T*)> func)
{
auto call = [](std::function<void(T*)> func, Object *ptr)
{
func(dynamic_cast<T *>(ptr));
};
return std::bind(call, func, std::placeholders::_1);
}
int main(int argc, const char * argv[]) {
Client c;
auto lambda = [](User* usr) {
std::cout<<"Lambda called\n";
};
c.on("Hello", createFunc<User>(lambda));
c.run();
return 0;
}
CreateFunc
基本上与:
template<typename T>
void callFuncHelper(std::function<void(T*)> func, Object *ptr)
{
func(dynamic_cast<T*>(ptr));
};
template<typename T>
typename std::enable_if<std::is_base_of<Object, T>::value, std::function<void(Object*)>>::type
createFunc(std::function<void(T*)> func)
{
return std::bind(callFuncHelper<T>, func, std::placeholders::_1);
}
的工作原理如下:
Client c;
auto lambda = [](User* usr) {
std::cout<<"Lambda called\n";
};
c.on("Hello", createFunc<User>(lambda));
c.run();
为什么呢?因为我无法找到推断lambda参数的另一种方法,所以你必须将其作为模板参数传递。