在执行内部重新分配std :: function对象

时间:2014-04-29 06:35:50

标签: c++ c++11 lambda std-function

我有一个std :: function对象我用作某个事件的回调。我正在为这个对象分配一个lambda,在其中,我将对象分配给另一个lambda mid执行。当我这样做时,我得到了一个段错误。这不是我可以做的事情吗?如果是这样,为什么?我将如何实现这一目标?

声明:

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();

2 个答案:

答案 0 :(得分:2)

标准没有指定在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

答案 1 :(得分:0)

简短回答

这取决于在(重新)赋值之后,被调用的lambda是否访问其任何非静态数据成员。如果确实如此,则会出现未定义的行为。否则,我相信不会发生任何不好的事情。

答案很长

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

作业调用template<class F> function& operator=(F&& f);,在20.8.11.2.1 / 18之前,具有

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

其中f绑定到l_2,而*this是分配给的std::function对象。目前,临时std::function持有l_2*this持有l_1。暂时保留swapl_1*this保留l_2(*)。然后临时被破坏,l_1也是如此。

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

  
    

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

  

Lambdas非静态数据成员对应其捕获。因此,如果您不访问它们,那么应该没问题。

Yakkanswer提出了另外一个重要的观点。据我了解,关注的问题是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)。

  

哪个basicallky说std::function::operator()调用l_1.operator()而不执行任何其他操作(至少没有任何可检测的内容)。

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