Why can't we capture this in a lambda that is the default parameter of a method?

177 views Asked by At

I'm attempting to capture the this pointer within a lambda function that serves as the default parameter for a method.

My goal is to invoke a method of this class from within the lambda, which necessitates capturing the this pointer.

However, the following code results in an error:

error C3639: a lambda that is part of a default argument can only have an init-capture

What is a common solution to overcome this limitation?

#include <functional>

class A{
    public:
    void B(std::function<void()> a = [this](){}); //error C3639: a lambda that is part of a default argument can only have an init-capture.
};
3

There are 3 answers

0
Jan Schultke On BEST ANSWER

The problem with the line ...

void B(std::function<void()> a = [this](){});

... is that the lambda expression is default argument to a function parameter. Function arguments are created at the call site, and there is no this that could be captured at that point. The C++ standard prohibits this in default arguments:

[ Note: The keyword this may not appear in a default argument of a member function; see [expr.prim.this]. [ Example:

class A {
  void f(A* p = this) { }           // error
};

— end example ]  — end note ]

- [dcl.fct.default] p8

This is just a note, but it is true for lambdas as well because:

A lambda-expression appearing in a default argument shall not implicitly or explicitly capture any entity.

- [expr.prim.lambda.capture] p9

Rationale for why this cannot be used in default arguments

A function call is processed as follows:

  1. initialize all the function arguments (possibly using default arguments)
  2. transfer control to the function (this is now available)
  3. execute the function body and create the returned object
  4. transfer control back to the caller (this is no longer available)
  5. destroy the function arguments

Note: the order of steps 4. and 5. is implementation-defined

You're trying to access this in step 1, which is too early, not to mention that lambdas in default arguments can't have captures in general.

Solution

Adding a second overload such as

void B() { B( [this] {} ); }

would fix the problem, but you would end up with two functions instead of one.

1
Caleth On

You can instead overload the function

class A{
    public:
    void B(std::function<void()> a);
    void B(){ B([this](){}); };
};
1
Afshin On

Hm, I think it makes sense that you cannot pass lamda with capture list as default argument. it creates dependencies that should not be there.

But you probably can achieve what you want to do with something like this:

#include <functional>

class A{
    public:
    void B(std::function<void()> a) {
        if (!a) {
            // do default. You have access to 'this' here.
            return;
        }
        // do non-default with 'a' argument
    }
};