tuket
tuket

Reputation: 3951

Why does this compile when passing a lambda in direct initialization and assignment but not with copy initialization?

Why doesn't assignment operator allow for a lambda expression when done in the same line the object is declared?

It seems to be working in MSVC though.

Test the code: 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([]() {

    });
}

Upvotes: 23

Views: 1026

Answers (2)

songyuanyao
songyuanyao

Reputation: 172964

Func f1 = []() {}; is copy initialization, which requires two user-defined implicit conversion to construct f1, the 1st one is from the lambda to the function pointer, the 2nd one is from the function pointer to Func. Only one user-defined implicit conversion is allowed in one conversion sequence so it fails.

(emphasis mine)

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution.

and

Implicit conversion sequence consists of the following, in this order:

1) zero or one standard conversion sequence;
2) zero or one user-defined conversion;
3) zero or one standard conversion sequence.

For f2 = []() {}; the appropriate assignment operator is tried to be called, Func has one and it expects the function pointer as the argument; only one implicit conversion from the lambda to function pointer is required and then it works well.

Func f3([]() {}); is direct initialization, the appropriate constructor is tried to be called, Func has one and it expects the function pointer as the argument. Then it's the same as f2.

You may get the point from the difference between copy initialization and direct initialization.

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.

Upvotes: 22

François Andrieux
François Andrieux

Reputation: 29022

Your first case involves two implicit conversions, lambda to void(*)() then void(*)() to Func. You can have at most 1 implicit conversion.

If you can eliminate one of the implicit conversions it should work fine. Here are some potential solutions you can try :

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

Upvotes: 8

Related Questions