我想动态更改类方法的行为,因此我实现了这些方法,该方法调用持有副本的 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 析构函数后,不仅可以在捕获列表中使用其他任何局部变量安全吗? 是否有建议的方式/模式以更安全的方式完成相同的行为(不包括大型开关/如果处于类状态)?
答案 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
。