glades
glades

Reputation: 4759

Pass lambda that captures "this" to obj in member initializer list

I have the following problem: I define a class task that lives within another class object. I want to save a closure inside that task class to invoke it later. Now how excactly am I supposed to initialize the task object within the container class? I can't use this at "top level" (e.g. when I use it in direct member initialization. On the other hand, when I decltype() an empty lambda it gives me the wrong type it seems as it doesn't close over this. Check out the following code:

Demo

#include <cstdio>

template <typename Callable>
struct task
{
    task(Callable fn)
    :   fn_{fn}
    { }

    auto do_stuff() {
        fn_();
    }

    Callable fn_;
};

struct container
{
    container()
        :  task_([this]() -> void { this->print(); })
    { }

    auto print() -> void {
        printf("Hello World!\n");
    }

    auto execute() {
        task_.do_stuff();
    }

    task<decltype([](){})> task_;
};

int main()
{
    container a;

    a.execute();
}

This yields specifically:

<source>:20:12: error: no matching function for call to 'task<container::<lambda()> >::task(container::container()::<lambda()>)'
   20 |         :  task_([this]() -> void { this->print(); })
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

How can I create a closure over this in direct member initialization or in the member initializer list?

Upvotes: 1

Views: 201

Answers (1)

lorro
lorro

Reputation: 10880

If you want to be able to assign different functionalities to this task_, you need type erasure: std::function<void()> task_;. Also, you need to have print() before ctor in your code.

If you only need one functionality, then task_ is a member function basically.

If neither of these suits you, you can still introduce a layer of indirection in the hierarchy, containerBase:

struct containerBase
{
    void print() {
        printf("Hello World!\n");
    }

    auto g() { return [this]() -> void { this->print(); }; }
};

struct container : containerBase
{
    container()
        :  task_(g())
    { }

public:
    auto execute() {
        task_.do_stuff();
    }

    task<decltype(std::declval<containerBase>().g())> task_;
};

Upvotes: 2

Related Questions