Search code examples
c++classc++11lambdastd-function

Question about custom conversion of "lambda []void ()->void"


TestCase2 and TestCase3 can compile normally. However, in TestCase1 I get the following error:

E0312, Custom conversion from "lambda []void ()->void" to
       "EventHandler" is not appropriate.

Why am I getting this error? I want to know how to solve it.

#include <functional>
#include <iostream>

class EventHandler
{
    std::function<void()> _func;

public:
    int id;
    static int counter;

    EventHandler() : id{ 0 } {}
    EventHandler(const std::function<void()>& func) : _func{ func }
    {
        id = ++EventHandler::counter;
    }
};

int EventHandler::counter = 0;

int main()
{
    EventHandler TestCase1 = []() {};
    EventHandler TestCase2([]() {});
    EventHandler TestCase3 = static_cast<std::function<void()>>([]() {});
}

Solution

  • Why am I getting this error?

    The lambda []() {} is not the same as std::function<void()>. That means

    decltype([]() {}) != std::function<void()>
    

    and it has to be implicitly or explicitly converted.

    At the line

    EventHandler TestCase1 = []() {};
    

    copy initialization take place, where compiler first has to convert the lambda to a std::function<void()> and then a EventHandler object. Compiler can not do double implicit conventions.

    Therefore, you need to be explicit here, like in TestCase3 for instance.


    I want to know how to solve it.

    One way is to provide a templated constructor (if you willing to)

    #include <type_traits> // std::is_convertible_v
    
    class EventHandler
    {
        std::function<void()> _func;
    public:
    
        template<typename Func> EventHandler(Func func)
            : _func{ func }
        {
            static_assert(std::is_convertible_v<Func, decltype(_func)>
                                                , "is not valid arg!");
            // ....
        }
        // or in C++20 with <concepts> header
        // template<typename Func> EventHandler(Func func)
        //     requires std::convertible_to<Func, decltype(_func)>
        //  : _func{ func }
        // { ... }
    };
    

    Now you can

    EventHandler TestCase1 = []() {}; // works
    

    Demo