Sal*_*rtz 7 c++ lambda c++11 c++14
我有一个方法,它接受一个参数,它是对基类的引用,并通过将方法实现包装在一个方法体的调用中排队 queue<function<void()>>
问题是我希望按值捕获方法的参数,以便队列中的每个lambda可以使用自己的副本执行.
但是如果我按值捕获,那么引用参数的lambda副本似乎会对它进行切片,而是留下基类副本而不是引用中的实际派生类.
如果我通过引用捕获参数,我确实得到了lambda中的实际派生类,但是obj可能超出了方法调用之间的范围,或者它的状态可以改变.
请注意,该方法应该是可重入的,但不是异步的,也不是并发的.
这是我的意思(省略队列)的一个例子:
struct BaseObj {
virtual ~BaseObj() = default;
};
struct DerivedObj : public BaseObj {
};
void someMethod(BaseObj& obj) {
// obj is of type BaseObj:
std::cout << "\nobj type:" << typeid(obj).name();
auto refLambda = [&] {
// captured obj is of type DerivedObj:
std::cout << "\nrefLambda::obj type:" << typeid(obj).name();
};
auto valLambda = [=] {
// captured obj is of type BaseObj:
// presumably because it was copied by value, which sliced it.
std::cout << "\nvalLambda::obj type:" << typeid(obj).name();
};
refLambda();
valLambda();
}
Run Code Online (Sandbox Code Playgroud)
调用方法时的输出如下:
DerivedObj obj{};
someMethod(obj);
Run Code Online (Sandbox Code Playgroud)
方法是:
obj type:10DerivedObj
refLambda::obj type:10DerivedObj
valLambda::obj type:7BaseObj
Run Code Online (Sandbox Code Playgroud)
截至目前,我设法在方法调用中保留派生类型的唯一方法是:
像这样:
DerivedObj* obj = new DerivedObj();
someMethod(*obj);
delete obj;
Run Code Online (Sandbox Code Playgroud)
但是我希望能够从调用代码堆栈中传递一个引用并且即使在someMethod发生触发另一个调用的内容时也可以someMethod.
有任何想法吗?
一种方法我想到了,但我不知道怎么办是,里面'的someMethod",动参数堆,执行拉姆达然后最后将其删除(因为调用者没有真正使用它调用此之后方法).但不确定这实际上是否真的很糟糕(我只考虑过它,因为这有点像Objective-C块所做的那样).
这是我到目前为止的解决方案:
void Object::broadcast(Event& event) {
auto frozenEvent = event.heapClone();
auto dispatchBlock = [=]() {
for (auto receiver : receivers) {
receiver.take(event);
}
delete frozenEvent;
_private->eventQueue.pop();
if (!_private->eventQueue.empty()) {
_private->eventQueue.front()();
}
};
_private->eventQueue.push(dispatchBlock);
if (_private->eventQueue.size() == 1) {
_private->eventQueue.front()();
}
}
Run Code Online (Sandbox Code Playgroud)
是的,我知道,我正在使用原始指针...(eeeeevil ....:p),但至少我可以使用ref参数保留方法的签名.
克隆方法是这样的:
template <class T>
struct ConcreteEvent : public Event {
virtual Event* heapClone() {
return new T(*(T*)this);
}
// .... more stuff.
};
Run Code Online (Sandbox Code Playgroud)
如果不进行一些侵入性的更改,似乎不可能实现您想要的结果。目前,您有一个调用者可以更改或销毁其对象,而不关心引用是否仍在队列中。对于这种经常打电话的人,您唯一的选择就是复印。创建 lambda 的函数不知道您要传递什么类型的对象,因此它不知道如何复制它。
有不同的方法可以解决您的问题:您可以通过让调用者持有 ashared_ptr并将共享指针复制到 lambda 中来让调用者意识到额外的引用。这解决了生命周期问题,但仍然取决于调用者是否修改对象。您还可以通过将函数设为模板,让编译器为每个派生类生成不同的排队函数。模板的每个实例都知道如何复制其特定类型。您已经驳回了这两个解决方案。我知道的还有另一种方法,即向基类添加虚拟克隆函数,并在派生类中实现该函数以创建堆副本。
| 归档时间: |
|
| 查看次数: |
473 次 |
| 最近记录: |