代码之家  ›  专栏  ›  技术社区  ›  tuket

为什么在直接初始化和赋值时传递lambda而不是在复制初始化时进行编译?

  •  23
  • tuket  · 技术社区  · 6 年前

    为什么赋值运算符不允许在声明对象的同一行中完成lambda表达式?

    不过,它似乎在msvc工作。

    测试代码: https://godbolt.org/g/n2Tih1

    class Func
    {
        typedef void(*func_type)();
        func_type m_f;
    public:
        Func() {}
        Func(func_type f) : m_f(f) {}
        Func operator=(func_type f) {
            m_f = f;
            return *this;
        }
    };
    
    int main()
    {
        // doesn't compile in GCC and clang, it does in MSVC
        Func f1 = []() {
    
        };
    
        // compiles!
        Func f2;
        f2 = []() {
    
        };
    
        // compiles!
        Func f3([]() {
    
        });
    }
    
    2 回复  |  直到 6 年前
        1
  •  22
  •   songyuanyao    6 年前

    Func f1 = []() {}; copy initialization ,它需要两个用户定义的隐式转换来构造 f1 ,第一个是从lambda到函数指针,第二个是从函数指针到 Func . 一个中只允许一个用户定义的隐式转换 conversion sequence 所以它失败了。

    (强调我的)

    如果t是类类型,而other类型的cv不合格版本不是t或派生自t,或者t是非类类型,但other类型是类类型, 检查可以从other类型转换为t的用户定义转换序列(如果t是类类型且转换函数可用,则转换为从t派生的类型) 通过过载分辨率来选择最佳方案。

    隐式转换序列按以下顺序组成:

    1)零或一个标准转换序列;
    2)零次或一次自定义转换;
    3)零或一个标准转换序列。

    为了 f2 = []() {}; 试图调用适当的赋值运算符, 芬克 有一个,它期望函数指针作为参数;只需要一个从lambda到函数指针的隐式转换,然后它就可以正常工作了。

    Func f3([]() {}); direct initialization ,将尝试调用相应的构造函数, 芬克 有一个,它期望函数指针作为参数。那就跟 f2 .

    您可以从复制初始化和直接初始化之间的区别中得出这一点。

    此外,复制初始化中的隐式转换必须直接从初始值设定项生成t,而直接初始化则需要从初始值设定项到t的构造函数的参数的隐式转换。

        2
  •  8
  •   François Andrieux    6 年前

    第一种情况涉及两个隐式转换,lambda to void(*)() 然后 无效(*)() Func . 最多可以有一个隐式转换。

    如果可以消除其中一个隐式转换,那么它应该可以正常工作。以下是一些您可以尝试的潜在解决方案:

    // Explicit cast to a function pointer
    Func f1 = static_cast<void(*)()>([]() {});
    
    // func_ptr is already a function pointer
    //  eliminating one of the implcit conversions
    void (*func_ptr)() = [](){};
    Func f2 = func_ptr;
    
    // The conversion from `void(*)()` is no longer implicit
    Func f3{ [](){} };