user11313931
user11313931

Reputation:

Class type undefined in static member lambda function in C++17

A while ago, I was trying to implement my own v-tables (for educational purposes). To do that I used function pointers and lambdas. While doing so, I stumbled across an issue. Instead of posting the whole v-table code I just wrote a Minimal Reproducible Example.

For some reason, it doesn't compile (C2027 - use of undefined type Test):

class Test
{
    int n;
    static inline auto f = [](Test* t)
    {
        return t->n;
    };
};

Even when I forward-declare Test like this: class Test;.

I am curious what causes this behavior because both int n and class Test are declared before the lambda.

I am using latest Visual Studio 2019 with C++17.

Upvotes: 2

Views: 530

Answers (2)

songyuanyao
songyuanyao

Reputation: 172924

You can't do this because the class is not a complete type yet at the point of the lambda expression.

[class.mem]

6 A complete-class context of a class is a

  • function body ([dcl.fct.def.general]),

  • default argument,

  • noexcept-specifier, or

  • default member initializer

within the member-specification of the class. [ Note: A complete-class context of a nested class is also a complete-class context of any enclosing class, if the nested class is defined within the member-specification of the enclosing class. — end note ]

7 A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. The class is regarded as complete within its complete-class contexts; otherwise it is regarded as incomplete within its own class member-specification.

Upvotes: 1

Nicol Bolas
Nicol Bolas

Reputation: 473447

Test is declared before the lambda, but it is not defined. The class definition is only finished at the end of the class itself, when you do };. Until then, it is an incomplete type.

And you cannot access members of an incomplete type. Or at least, not via an object instance the way you do in your lambda (you can talk about Test::n at that point, but you can't take a Test* and do ->n on it).

Now, you might say that if you made f a normal static member, you could easily put the definition of the class there and it would work. This is because C++ has a special rule for the definition of member functions of a class. Namely, the bodies of these functions are considered to be defined as if they were placed immediately after the class definition. Thus, they can internally use the class as though it were a complete type, since those function bodies will be defined in a place where they are a complete type.

Your lambda is not a member function; it's a lambda function being assigned to a class-static variable. The initializers of static variables don't get this special treatment, so Test is considered incomplete inside of the lambda's function body.

Upvotes: 1

Related Questions