关于 "lambda []void ()->void" 的自定义转换的问题

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

TestCase2TestCase3可以正常编译。但是,在 TestCase1 中出现以下错误:

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

为什么会出现此错误?我想知道怎么解决。

#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()>>([]() {});
}

Why am I getting this error?

lambda []() {}std::function<void()> 不同。也就是说

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

并且必须隐式或显式转换

EventHandler TestCase1 = []() {};

copy initialization take place,其中编译器首先必须将 lambda 转换为 std::function<void()>,然后是 EventHandler 对象。编译器不能执行双重隐式约定。

因此,您需要在此处明确说明,例如 TestCase3


I want to know how to solve it.

一种方法是提供模板化构造函数(如果您愿意)

#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 }
    // { ... }
};

现在你可以

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

Demo

案例一

这里我们考虑语句:

EventHandler TestCase1 = []() {};//this is copy initialization

以上语句使用了复制初始化

并且来自 copy initialization's documentation

In addition, the implicit conversion in copy-initialization must produce T directly from the initializer, while, e.g. direct-initialization expects an implicit conversion from the initializer to an argument of T's constructor.

并且由于 EventHandler 不能直接从右侧的初始化程序 lambda 生成,因此根据上面引用的声明,TestCase1 的第一种情况是不允许的。


案例二

这里我们考虑语句,

EventHandler TestCase2([]() {});//this is direct initialization

上面使用的是直接初始化,由于有隐式转换可用(使用转换构造函数),这次按照上面引用的说法是允许的