2

I want to register a callback that may remove itself from its container. But I found the captured variables seem to be cleaned up. the code like this:

class A {
    int val_;
public:
    A(int val) : val_(val) {}
    void foo() {
        std::cout << val_ << std::endl;
    }
};

int main()
{
    const int func_index = 1;
    std::unordered_map<int, std::function<void(void)>> container;
    A a(10);
    container.insert(std::make_pair(func_index, [&container, func_index, &a] () {
        container.erase(func_index);
        for (int i = 0; i < 10; i++) {
            a.foo();
        }
    }));
    container[func_index]();
    return 0;
}

the solution, move the std::function object into the lambda to delay its destruction:

class A {
    int val_;
public:
    A(int val) : val_(val) {}
    void foo() {
        std::cout << val_ << std::endl;
    }
};

int main()
{
    const int func_index = 1;
    std::unordered_map<int, std::function<void(void)>> container;
    A a(10);
    container.insert(std::make_pair(func_index, [&container, func_index, &a] () {
        std::function<void(void)> self = std::move(container[func_index]);
        container.erase(func_index);
        for (int i = 0; i < 10; i++) {
            a.foo();
        }
    }));
    container[func_index]();
    return 0;
}
QingYun
  • 859
  • 2
  • 8
  • 16

1 Answers1

5

Your code has undefined behaviour, since you are accessing the closure after deleting it from the container. You should remove the closure from the container after doing work with it:

class A {
    int val_;
public:
    A(int val) : val_(val) {}
    void foo() {
        std::cout << val_ << std::endl;
    }
};

int main()
{
    const int func_index = 1;
    std::unordered_map<int, std::function<void(void)>> container;
    A a(10);
    container.insert(std::make_pair(func_index, [&container, func_index, &a] () {
        for (int i = 0; i < 10; i++) {
            a.foo();
        }
        container.erase(func_index);
    }));
    container[func_index]();
    return 0;
}
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • Thanks! But I have to put `erase` at the beginning of the lambda. Is there any way to disable indexing this function from container until I can actually remove it at the end of the lambda? – QingYun Jun 27 '13 at 16:18
  • 1
    @user2134183: You could always just save the relevant data to local variables before calling `erase`. The important thing is to *not* access captured variables after erasing the closure. – Mankarse Jun 27 '13 at 16:19
  • 1
    Thanks! I found another way to fix it! I moved the `std::function` object in the container into the lambda and remove the empty object from the container. Just add `std::function self = std::move(container[func_index]);` at the beginning of the lambda! – QingYun Jun 27 '13 at 17:07