仍执行时调用std :: function析构函数

时间:2019-02-12 10:04:24

标签: c++ c++11

我想动态更改类方法的行为,因此我实现了这些方法,该方法调用持有副本的 std :: function operator()一个lambda函数的函数,它一次取决于一些仅在类构造之后才知道的值。 Lambda更改了类的状态,因此它们重置了容纳所有动态方法行为的容器。
执行以上想法,重置容器后,我无法访问lamba的捕获列表。
以下代码段重现了该问题:

public func Encoder() -> [UInt8] {

            let cborWrapper : CBOR = [

                "D": testEncodeIndefiniteByteStrings()
            ]
            return cborWrapper.encode()
}


func testEncodeIndefiniteByteStrings()->[UInt8] {

    var arrOfCBOR = [CBOR]()

    let cbor: [UInt8] = [0x5f, 0x41, 0xf0, 0x41, 0xff, 0xff]
    let encoded = CBOR.encodeByteStringStreamStart()
        + CBOR.encode(cbor, asByteString: true)
        + CBOR.encodeStreamEnd()
    // But here i want key value pair too like "d":CBOR.encode(cbor,asByteString: true)


    return encoded
}

产生输出:

std::vector< std::function<void(std::string)> > vector;

int main() {

   //Change class state when variable value will be known
   std::string variableValue = "hello";

   auto function = [variableValue](std::string arg) {
     std::cout <<"From capture list, before: "<< variableValue << std::endl;
     std::cout <<"From arg,          before: " << arg << std::endl;

     vector.clear();

     std::cout << "From capture list, after: " << variableValue << std::endl;
     std::cout << "From arg,          after: " << arg << std::endl;
   };

   vector.push_back(function);

   //Dynamic method execution
   vector[0](variableValue);

   return 0;
}

vector 清除后, variableValue 无效。

捕获列表失效是否是预期结果? 在调用 std :: function 析构函数后,不仅可以在捕获列表中使用其他任何局部变量安全吗? 是否有建议的方式/模式以更安全的方式完成相同的行为(不包括大型开关/如果处于类状态)?

2 个答案:

答案 0 :(得分:2)

我们可以摆脱此问题的std::function,lambda和vector。由于lambda只是具有函数调用运算符的类的语法糖,因此您的测试用例实际上与此相同:

struct Foo
{
   std::string variableValue = "hello";

   void bar(std::string arg)
   {
      std::cout <<"From capture list, before: "<< variableValue << std::endl;
      std::cout <<"From arg,          before: " << arg << std::endl;

      delete this;  // ugrh

      std::cout << "From capture list, after: " << variableValue << std::endl;
      std::cout << "From arg,          after: " << arg << std::endl;
   }
};


int main()
{
    Foo* ptr = new Foo();
    ptr->bar(variableValue);
}

函数参数很好,因为它是一个副本,但是在delete this后,成员Foo::variableValue不再存在,因此您的程序因尝试使用它而具有未定义的行为。

通常的看法是,continuing to run the function itself is legal(因为函数定义不是对象并且不能被“删除”;它们只是程序的基本属性),只要您足够独立地处理封装类的成员

但是,除非您确实需要,否则我建议您避免使用此模式。人们容易混淆类的所有权责任(即使“您的类”是从lambda表达式自动生成的!)。


  

捕获列表失效是否是预期结果?

是的

  

调用std :: function析构函数后,不仅可以在捕获列表中使用任何其他局部变量安全吗?

是的

  

是否有建议的方式/模式以更安全的方式完成相同的行为(不包括大型开关/如果处于类状态)?

如果不了解您要尝试的操作,就无法确定地说。但是您可以尝试在向量中存储shared_ptr来代替。。。请注意不要在lambda本身中捕获shared_ptr,否则它将永远被清除起来!捕获weak_ptr可能会更好。可以将其“转换”为lambda主体内的shared_ptr,这将在该主体的整个生命周期内保护lambda的生命。

答案 1 :(得分:0)

std::function's destructor destroys the object's target if the object is non-empty,其中the target is the wrapped callable object

在您的情况下,目标是lambda表达式。使用lambda表达式时,编译器会生成一个"non-union non-aggregate class type" that contains the captures-by-value as data members and has operator() as a member function

执行vector.clear()时,将运行其元素的析构函数,因此将运行闭包的按值捕获(它们是成员变量)的析构函数。

对于引用捕获,"the reference variable's lifetime ends when the lifetime of the closure object ends."

因此,在std::function的析构函数运行之后,无论是通过值还是通过引用访问任何捕获都是不安全的。

实际的operator()呢? "Functions are not objects,",因此他们没有lifetimes。因此,只要您不访问任何捕获,在析构函数运行之后仅执行operator()就可以了。参见the conditions under which one can safely delete this