当用新值替换自身时,回调对象在执行时会自行销毁

时间:2017-02-24 14:48:36

标签: c++ algorithm callback control-flow object-lifetime

考虑以下情况:

struct A {
    // `unique_function` is like std::function but does not
    // require its target to be Copyable, and is in turn
    // itself not Copyable either.
    unique_function<void()> callback = []{};

    void set_callback(unique_function<void()> new_callback) {
        callback = std::move(new_callback);
        callback();
    }
};

int main() {
    A a;
    a.set_callback([&, x = std::make_unique<int>(42)]{
        std::cout << "Ok at this point, *x = " << *x << ".\n";
        a.set_callback([]{}); // This destroys enclosing lambda
        // while is is being executed, thus messing things up.
        // Captures are not legal to access here.
    });
}

在这种情况下,预期的语义是在设置它们时调用所有回调,并在最高callback返回时将最后一个设置存储在set_callback内。

我可以看到的解决方案:

  1. unique_function<void()>替换为可复制的内容,例如std::shared_ptr<unique_function<void()>>或专门的shared_function<void()>,然后调用副本:

    shared_function<void()> callback = []{};
    
    void set_callback(shared_function<void()> new_callback) {
        callback = std::move(new_callback);
        auto callback_copy = callback;
        callback_copy();
    }
    

    缺点:

    1. 开销或参考计数。
  2. 有一个列表,并在执行之前附加回调。当返回页首set_callback时,callbacks.back()是最后一个回调集,其余的可以被删除。这需要额外的计数器跟踪当前set_callback次呼叫的深度。

    std::deque<unique_function<void()>> callbacks;
    std::size_t depth = 0;
    
    struct depth_controller {
        std::size_t& depth;
        depth_controller(std::size_t& d) : depth(d) { ++depth; }
        ~depth_controller() { --depth; }
    };
    
    void set_callback(shared_function<void()> new_callback) {
        callbacks.emplace_back(std::move(new_callback));
        {
            depth_controller dctl{depth};
            callbacks.back()();
        }
        if (depth > 0) { return; }
        callbacks.erase(callbacks.begin(),
                        std::prev(callbacks.end()));
    }
    

    缺点:

    1. 回调列表和深度计数器的开销。
  3. 我们可以做得更好吗?

0 个答案:

没有答案