Reputation:
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
Reputation: 172924
You can't do this because the class is not a complete type yet at the point of the lambda expression.
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
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