10

If a lambda is capture-less, its closure type has conversion operator to function pointer. For example,

auto l = []{};
using F = decltype(+l); //pointer to function type
constexpr F p = l;

And if a class inherits closure type, it is reasonable to expect that conversion operator is inherited as well. But in MSVC compiler it is not, and one has to define it explicitly:

using L = decltype(l);
struct A : L {
    //only required for MSVC:
    constexpr operator F() { return static_cast<L&>(*this); }
};
constexpr F q = A{};

Without operator A::F() MSVC complains:

error C2440: 'initializing': cannot convert from 'A' to 'const F'
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

Online demo.

I thought that it is somehow related to the presence of several conversion operators in MSVC for distinct calling conventions, as described here. But manually created class with similar conversions permits inheritance of the conversion operators, online demo.

Why the inheritance does not work for closure types then as expected?

3
  • This is almost certainly an MSVC bug (or a strict non-conformance) regarding the visibility of synthesised members in lambda closure types. Commented Feb 8 at 21:10
  • +l makes F a void(*)() instead of a class type so that could be what messes it up. I don't think it should though. Commented Feb 8 at 21:11
  • This is not a language rule and not related to calling conventions. It’s a MSVC implementation bug / limitation. According to the standard, a capture-less lambda’s closure type has a public, non-explicit, const conversion operator to a function pointer. That operator is a normal member conversion function, and member conversion functions are inherited like other non-static members. Commented Feb 8 at 21:56

1 Answer 1

8

The program is well-formed and msvc is wrong to reject the program, as explained below.

First, from expr.prim.lambda#8:

The closure type for a non-generic lambda-expression with no lambda-capture whose constraints (if any) are satisfied has a conversion function to pointer to function with C++ language linkage having the same parameter and return types as the closure type's function call operator. The conversion is to “pointer to noexcept function” if the function call operator has a non-throwing exception specification. If the function call operator is a static member function, then the value returned by this conversion function is the address of the function call operator. Otherwise, the value returned by this conversion function is the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type. F is a constexpr function if the function call operator is a constexpr function and is an immediate function if the function call operator is an immediate function.

This means that L already has a non-static conversion function that returns the address of such a function F which when invoked has the effect of invoking L's function call operator.

Next, since A inherits from L, it will also inherit the above mentioned conversion function as per class.derived:

Members of a base class are also members of the derived class.

Basically, we don't need to define/declare the conversion function again in the derived class as it is already a member of derived class.


Here is the newly submitted msvc bug:

https://developercommunity.visualstudio.com/t/MSVC-rejects-valid-program-involving-inh/11040924

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.