Guillaume Racicot
Guillaume Racicot

Reputation: 41750

Why is the enclosing type of a static lambda member incomplete?

I tried to do something similar today. I was surprised it didn't compile.

struct Test {
//  v----- Remove me to compile
    //  /*
    static constexpr auto get_test1 = [](Test const& self) {
        return self.test; // error, Test is incomplete
    };
    // */

    // Handwritten version of the lambda
    struct {
        constexpr auto operator() (Test const& self) const {
            return self.test; // ok
        }
    }
    static constexpr get_test2{};

    int test;
};

Live example

It says that the Test type is incomplete in the scope. Yet the handwritten version of the lambda does indeed work. What is the technical reason for that? Is it an oversight in the standard, or is there a specific wording that makes Test incomplete in the lambda?

Upvotes: 15

Views: 427

Answers (2)

bolov
bolov

Reputation: 75688

This is what I could find:

§5.1.2 Lambda expressions [expr.prim.lambda]

  1. [...] [ Note: Names referenced in the lambda-declarator are looked up in the context in which the lambda-expression appears. —end note ]

  2. The lambda-expression’s compound-statement yields the function-body (8.4) of the function call operator, but for purposes of name lookup (3.4), [...] the compound-statement is considered in the context of the lambda-expression.

If I am not misreading the standard this means that Test and self both in the parameter as well in the body are looked/considered in the context of the lambda, which is the class scope in which Test is incomplete.

As for why it is allowed with the nested class:

§9.2 Class members [class.mem]

  1. A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the classspecifier . Within the class member-specification, the class is regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (12.9), exception-specification s, and brace-or-equal-initializer s for non-static data members (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

Upvotes: 3

metalfox
metalfox

Reputation: 6731

I think that your compiler is correct. Test is still incomplete. The processing of such a data member’s initializer (which is what requires the type to be complete) is not deferred until the end of the class definition.

I couldn't find the part of the Standard that deals with this issue, but I remember it was raised as a National Body comment of the C++17 draft (US 24, P0488R0):

The current specification prohibits constexpr static data members that are of the same type as the enclosing class.

Example:

struct A {
   int val;
   static constexpr A cst = { 42 };  // error
}; 

Apparently, lifting completely the restriction may have caused some breaking changes so that the comment "did not increase consensus".

Upvotes: 0

Related Questions