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

时间:2018-06-15 15:18:44

标签: c++ c++11 lambda initialization variable-assignment

为什么赋值运算符在声明对象的同一行中完成时不允许使用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 个答案:

答案 0 :(得分:22)

Func f1 = []() {};copy initialization,它需要两个用户定义的隐式转换来构造f1,第一个是从lambda到函数指针,第二个来自函数指向Func的指针。

(强调我的)

  

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

  

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

     

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

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

Func f3([]() {});conversion sequence,尝试调用相应的构造函数,Func有一个,它希望函数指针作为参数。然后它与f2相同。

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

  

此外,复制初始化中的隐式转换必须直接从初始化器生成T,而例如, direct-initialization期望从初始化器到T的构造函数的参数的隐式转换。

答案 1 :(得分:8)

您的第一个案例涉及两个隐式转换,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{ [](){} };