在执行std::函数对象时重新分配该对象

Re-assigning an std::function object while inside its execution

本文关键字:对象 分配 新分配 执行 std 函数      更新时间:2023-10-16

我有一个std::function对象,我正在使用它作为某个事件的回调。我将一个lambda分配给这个对象,在这个对象中,我在执行过程中将该对象分配给不同的lambda。当我做这件事的时候,我会犯一个错误。这不是允许我做的事情吗?如果是,为什么?我将如何实现这一目标?

声明:

std::function<void(Data *)> doCallback;

调用:

//
// This gets called after a sendDataRequest call returns with data
//
void onIncomingData(Data *data)
{
    if ( doCallback )
    {
        doCallback(data);
    }
}

任务:

doCallback =
    [=](Data *data)
    {
        //
        // Change the callback within itself because we want to do 
        // something else after getting one request  
        //
        doCallback =
            [=](Data *data2)
            {
                ... do some work ...
            };
        sendDataRequest();
    };
sendDataRequest();

标准没有指定函数在std::function::operator()的操作中何时使用其内部状态对象。在实践中,一些实现在调用之后使用它。

所以你所做的是未定义的行为,尤其是崩溃。

struct bob {
  std::function<void()> task;
  std::function<void()> next_task;
  void operator()(){
    next_task=task;
    task();
    task=std::move(next_task);
  }
}

现在,如果您想更改下一次在bob()中调用bob时发生的情况,只需设置next_task即可。

简短回答

这取决于(重新)赋值后,被调用的lambda是否访问其任何非静态数据成员。如果是这样,则会得到未定义的行为。否则,我相信不会有什么不好的事情发生。

长答案

在OP的示例中,调用由std::function对象持有的lambda对象(此处用l_1表示),并且在执行过程中,std::function对象被分配给另一个lambda对象,此处用l_2表示。

分配调用template<class F> function& operator=(F&& f);,到20.8.11.2.1/18,CCD_9具有的效果

function(std::forward<F>(f)).swap(*this);

其中f绑定到l_2*this是被分配到的std::function对象。此时,临时std::function保存l_2*this保存l_1。在CCD_ 18之后,临时保持CCD_ 19并且CCD_。然后临时性被破坏,l_1也被破坏。

总之,在l_1上运行operator()时,此对象会被销毁。然后根据12.7/1

对于具有非平凡构造函数的对象,在构造函数开始执行之前引用该对象的任何非静态成员或基类会导致未定义的行为对于具有非平凡析构函数的对象,在析构函数完成执行后引用该对象的任何非静态成员或基类会导致未定义的行为

Lambdas非静态数据成员对应其捕获。所以,如果你不访问它们,那应该没问题。

亚克的回答提出了一个更重要的观点。据我所知,问题是std::function::operator()在将呼叫转发给l_1后,是否试图访问l_1(现在已经失效)?我不认为是这样,因为std::function::operator()的影响并不意味着这一点。事实上,20.8.11.2.4说,这次通话的效果是

INVOKE(f, std::forward<ArgTypes>(args)..., R)(20.8.2),其中f*this的目标对象(20.8.1)。

基本上说CCD_ 32调用CCD_。

(*)我正在详细说明交换是如何发生的,但这个想法仍然有效。(例如,如果临时保存l_1的副本而不是指向它的指针,该怎么办?)