将多个类型传递给std :: function C ++

时间:2017-06-04 13:03:39

标签: c++ c++11 lambda

我目前正在处理一个小型库并遇到错误。我基本上要做的是有这样的语法:

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 先感谢您。

2 个答案:

答案 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参数的另一种方法,所以你必须将其作为模板参数传递。