C++ 在执行时重新分配std::function对象

C++ 在执行时重新分配std::function对象,c++,c++11,lambda,std-function,C++,C++11,Lambda,Std Function,我有一个std::function对象,用作某个事件的回调。我正在给这个对象分配一个lambda,在这个对象中,我在执行过程中将对象分配给另一个lambda。当我这样做的时候,我会犯错误。这不是我可以做的吗?若然,原因为何?我将如何实现这一目标 声明: std::function<void(Data *)> doCallback; 任务: doCallback = [=](Data *data) { // // Change the

我有一个std::function对象,用作某个事件的回调。我正在给这个对象分配一个lambda,在这个对象中,我在执行过程中将对象分配给另一个lambda。当我这样做的时候,我会犯错误。这不是我可以做的吗?若然,原因为何?我将如何实现这一目标

声明:

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

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);
  }
}
struct-bob{
功能任务;
std::函数下一个任务;
void运算符()(){
下一个任务=任务;
任务();
任务=标准::移动(下一个任务);
}
}

现在,如果您想更改在
bob()
中下次调用
bob
时发生的情况,只需设置
next\u task

,标准不会指定函数在
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);
  }
}
struct-bob{
功能任务;
std::函数下一个任务;
void运算符()(){
下一个任务=任务;
任务();
任务=标准::移动(下一个任务);
}
}

现在,如果您想更改在
bob()
中下次调用
bob
时发生的情况,只需设置
next\u任务

简短回答

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

长答案

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

赋值调用
模板函数&运算符=(F&&F)根据20.8.11.2.1/18,其具有以下效果:

function(std::forward<F>(f)).swap(*this);
函数(std::forward(f)).swap(*this);
其中,
f
绑定到
l_2
*此
是分配给的
std::function
对象。此时,临时
std::函数
保持
lu 2
,而
*此
保持
lu 1
。在
swap
之后,临时持有
l_1
*此
持有
l_2
(*)。然后临时文件被销毁,
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(args)…,R)
(20.8.2),其中
f
*此
的目标对象(20.8.1)

basicallky说,
std::function::operator()
调用
l_1.operator()
,并且不做任何其他事情(至少,不做任何可检测的事情)


(*)我将详细介绍交换是如何进行的,但这个想法仍然有效。(例如,如果临时人员持有一份
l_1
的副本,而没有指向它的指针怎么办?

简短回答

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

长答案

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

赋值调用
模板函数&运算符=(F&&F)根据20.8.11.2.1/18,其具有以下效果:

function(std::forward<F>(f)).swap(*this);
函数(std::forward(f)).swap(*this);
其中,
f
绑定到
l_2
*此
是分配给的
std::function
对象。此时,临时
std::函数
保持
lu 2
,而
*此
保持
lu 1
。在
swap
之后,临时持有
l_1
*此
持有
l_2
(*)。然后临时文件被销毁,
l_1

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

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