c++lambdathisdefault-arguments

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


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.
};

Solution

  • 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.