Search code examples
c++lambdaauto

Using auto specifier in std::function and lambdas


I have a class named Handler which stores some lambdas. What I want to do is to have a std::vector of std::function that stores all my events, for example. I really can't figure out why lambdas doesn't work as I expected.

Here's the handler.h:

class Handler
{
public:
    Handler();
    ~Handler();

    void Register(const char* outcome, std::function<auto()> lambda);
    void Trigger(const char* outcome);

private:
    std::vector<int> identifier;
    std::vector<char*> outcome;
    std::vector<std::function<auto()>> func;
};

And handler.cpp:

Handler::Handler()
{
    //ctor stuff here
}


Handler::~Handler()
{
    this->func.clear();
    this->outcome.clear();
    this->identifier.clear();
    //...
}

void Handler::Register(const char* outcome, std::function<auto()> lambda)
{
    static int identifier = 0;
    identifier++;
    this->outcome.push_back((char*)outcome);
    this->identifier.push_back(identifier);
    this->func.push_back(lambda);
    //Sort outcome
}

void Handler::Trigger(const char * outcome)
{
    int i;
    for (i = 0; i < this->identifier.size(); i++)
    {
        if (!strcmp(outcome, this->outcome.at(i)))
            break;
    }
    this->func[i]();
}

However, if I specify lambdas in a Handler::Register it wont let me throwing no suitable user-defined conversion from "lambda []void ()->void" to "std::function<auto()> exists. In this example I use void return type but other types also error, I don't understand why can't the template from std::function deduce it out, if it is what's happening.

Handler* events = new Handler();

    events->Register("Up", [=]() -> void { //Error here!
        //do stuff
        //return something?
    });

Is there any other way to do this, like without overloading Handler::Register?


Solution

  • auto is not a type, so std::function<auto()> is not a type either. From how you are using it, std::function<void()> is probably what you want.

    There are other problems with your code, as noted in the comments, so I would change Handler to this

    class Handler
    {
    public:
        Handler();
        // default ~Handler is fine
    
        void Register(std::string outcome, std::function<void()> lambda);
        void Trigger(const std::string & outcome outcome) const;
        void Trigger(std::size_t index) const;
    
    private:
        using Outcomes = std::map<std::string, std::function<void()>/*, custom string comparator ?*/>;
        std::vector<Outcomes::iterator> identifier;
        Outcomes outcomes;
    };
    
    void Handler::Register(std::string outcome, std::function<void()> func)
    {
        auto emplaced = outcomes.emplace(std::move(outcome), std::move(func));
        identifier.push_back(emplaced.first);
    }
    
    void Handler::Trigger(const std::string & outcome) const
    {
        outcomes.at(outcome)();
    }
    
    void Handler::Trigger(std::size_t index) const
    {
        identifier[index]->second();
    }