MSVC behaves different about default constructor of closure type in C++20

Viewed 140

The standard says

The closure type associated with a lambda-expression has no default constructor if the lambda-expression has a lambda-capture and a defaulted default constructor otherwise. It has a deleted copy assignment operator if the lambda-expression has a lambda-capture and defaulted copy and move assignment operators otherwise. It has a defaulted copy constructor and a defaulted move constructor (15.8). [ Note: These special member functions are implicitly defined as usual, and might therefore be defined as deleted. — end note ]

Cppreference specifically says that (emphasis mine)

If no captures are specified, the closure type has a defaulted default constructor. Otherwise, it has no default constructor (this includes the case when there is a capture-default, even if it does not actually capture anything).

If no captures are specified, the closure type has a defaulted copy assignment operator and a defaulted move assignment operator. Otherwise, it has a deleted copy assignment operator (this includes the case when there is a capture-default, even if it does not actually capture anything).

So the following must be valid.

auto lambda = [&](){};

static_assert(!std::is_default_constructible<decltype(lambda)>::value);
static_assert(!std::is_assignable<decltype(lambda), decltype(lambda)>::value);

But MSVC says they are default_constructible, etc.

https://godbolt.org/z/E6EW3rMcE

Since the paper didn't specifically mention about capture-default but not capturing in actual, I wonder if this is MSVC defect or allowed to be implementation-defined.


Update

I've reported this bug to Microsoft, and it will be fixed on the coming release link.

1 Answers

The standard is pretty clear here. In [expr.prim.lambda.closure]/13:

The closure type associated with a lambda-expression has no default constructor if the lambda-expression has a lambda-capture and a defaulted default constructor otherwise.

This rule is based on the lexical makeup of the lambda, not the semantic analysis that we do to decide if there is anything captured. It is a grammatical distinction.

If a lambda starts with [], then it has no lambda-capture, and thus has a defaulted default constructor.

If a lambda starts with [&], then it has a lambda-capture, and thus has no default constructor - regardless of whether anything is captured. It doesn't matter if anything is captured or not.

The clarification that cppreference adds here is correct, and helpful. The lambda [&](){} is not default constructible (or, by the same logic, assignable). So, yes this is a MSVC bug.


Note that this is the same kind of rule that we have to determine if a lambda can be converted to a function pointer: whether or not there is a lambda-capture, not whether or not there is any capture. Thus, [](){} is convertible to void(*)() but [&](){} is not.

Related