为啥在直接初始化和赋值中传递 lambda 而不是复制初始化时会编译?

Posted

技术标签:

【中文标题】为啥在直接初始化和赋值中传递 lambda 而不是复制初始化时会编译?【英文标题】:Why does this compile when passing a lambda in direct initialization and assignment but not with copy initialization?为什么在直接初始化和赋值中传递 lambda 而不是复制初始化时会编译? 【发布时间】:2018-11-25 11:17:05 【问题描述】:

为什么赋值运算符不允许在声明对象的同一行中使用 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([]() 

    );

【问题讨论】:

试试Func f4 = +[](); :-) @Jarod42 太棒了!为什么? o.0 它类似于 François Andrieux 的回答中的static_cast<void(*)()>([]() ) 哦,我明白了:***.com/questions/18889028/… 【参考方案1】:

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

(强调我的)

如果 T 是类类型,并且 other 的类型的 cv 非限定版本不是 T 或派生自 T,或者如果 T 是非类类型,但 other 的类型是类类型,检查可以从 other 的类型转换为 T 的用户定义的转换序列(如果 T 是类类型并且转换函数可用,则转换为从 T 派生的类型) 并通过以下方式选择最佳的转换序列重载决议。

隐式转换序列包含以下内容,按此顺序:

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

对于f2 = []() ;,尝试调用适当的赋值运算符,Func 有一个,它期望函数指针作为参数;只需一次从 lambda 到函数指针的隐式转换,就可以正常工作。

Func f3([]() ); 是direct initialization,尝试调用适当的构造函数,Func 有一个,它期望函数指针作为参数。那么就和f2一样。

你可以从拷贝初始化和直接初始化的区别中理解。

此外,复制初始化中的隐式转换必须直接从初始化程序生成 T,而例如直接初始化需要从初始化程序隐式转换为 T 的构造函数的参数。

【讨论】:

【参考方案2】:

您的第一个案例涉及两个隐式转换,lambda 到 void(*)() 然后 void(*)()Func。您最多可以进行 1 次隐式转换。

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

// 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 []() ;

【讨论】:

我建议让func_typeFunc 中公开,然后您可以执行Func f1 = static_cast&lt;Func::func_type&gt;([]());Func::func_type func_ptr = [](); 之类的操作

以上是关于为啥在直接初始化和赋值中传递 lambda 而不是复制初始化时会编译?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用闭包进行赋值而不是直接为键赋值?

为啥 Python 的 `lambda` 表达式中不允许赋值?

为啥在 ASP.NET MVC 中使用 lambdas 而不是反射?

为啥在 lambda 中使用 ++i 而不是 i++

为啥 lambda 参数在 C++11 中以只读方式传递?

为啥 std::apply 可以调用 lambda 而不是等效的模板函数?