2

The following code does not compile, giving an error "invalid use of non-static data member"

struct A {
    int m_i;
    
    void f() const;
};

void A::f() const
{
    auto l = [&](int x = m_i) { };
}

int main()
{
    A a{1};
}

Now, I can understand that in a definition of a method of A one cannot use a non-static field as default parameter, because they are not know at the place of the definition. Indeed, in general default arguments cannot be non-static.

However, in the code above, the lambda l is defined inside a non-static method A::f(), so it seems to me that in the definition it should be able to access to the non-static member A::m_i.

So the question is: why this limitation in the language? What would be the problem in allowing a lambda to have default arguments that are non-static? Provided that the lambda is defined in a non-static environment?

Note: This question is not yet another question on "why this code does not compile"... it is more about, if there is some subtle reason why the language could not be allowed to consider a code like above.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • 1
    You are mean to capture `this` is you want to use members of the class. That makes sure if you return the lambda from the function you still have a valid handle. – NathanOliver May 05 '23 at 13:01
  • [Only objects with automatic storage duration can be captured by a lambda in C++11 (i.e. local variables and function parameters). If you want the effect of capturing a non-static class data member, you can either capture the this pointer](https://stackoverflow.com/a/23810162) – Jason May 05 '23 at 13:05
  • 2
    @NathanOliver I almost fell for it too, but this is not about capture it is about default initializing a lambda function argument... I never thought about doing this and can't quickly find an answer in the standard or cppreference. – Pepijn Kramer May 05 '23 at 13:06
  • Unrelated: [Using member variable in lambda capture list inside a member function](https://stackoverflow.com/questions/7895879/using-member-variable-in-lambda-capture-list-inside-a-member-function) and [Why can't a data member be in a lambda capture list](https://stackoverflow.com/questions/23803152/why-cant-a-data-member-be-in-a-lambda-capture-list) – Jason May 05 '23 at 13:07
  • 1
    @Jason OP does not use member variable in lambda capture list. This question is about default argument. – user7860670 May 05 '23 at 13:08
  • @user7860670 Ah, right – Jason May 05 '23 at 13:09
  • default arguments are replaced at the call site. At the call site there is not necessarily a `this->m_i` – 463035818_is_not_an_ai May 05 '23 at 13:13

1 Answers1

4

The use of default argument implies that at the point of invocation function declaration with the default argument is available and the expression used is valid. What would happen if we some non-static class field such as m_i was used there? For example if we return l and then invoke it inside of main:

struct A
{
    int m_i;
    
    auto f()
    {
       auto l = [&](int x = m_i) { };
       return l;
    }
};

int main()
{
    A a{1};
    auto l{a.f()} ;
    l();
}
  1. Lambda can't be forward declared so the definition must be available at the point of invocation.

  2. Inside of main expression m_i used a default argument will be invalid.

Hypothetically we could introduce some new fancy set of rules to the (already overly complex) language allowing use of non-static member variable as default argument of lambda by utilizing this captured by lambda so invocation will be converted by compiler into something like l.(l.__this__->m_i);. However if we access this value through captured this pointer then the value may vary from invocation to invocation which to me seems to be rather confusing. The other possible alternative is to access unmodified m_i value through some implicitly captured data member, however this would break existing set of rules prohibiting capture of data members.

user7860670
  • 35,849
  • 4
  • 58
  • 84