问题是我希望按值捕获方法的参数,以便队列中的每个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(); }
DerivedObj obj{}; someMethod(obj);
方法是:
obj type:10DerivedObj refLambda::obj type:10DerivedObj valLambda::obj type:7BaSEObj
>从调用代码传递堆分配的对象.
>通过lambda中的引用捕获.
>确保不要在调用代码中改变原始内容.
>最后在方法返回后删除堆obj.
像这样:
DerivedObj* obj = new DerivedObj(); someMethod(*obj); delete obj;
但我希望能够从调用代码堆栈中传递一个引用并且即使在someMethod内部发生触发另一个someMethod调用的事情也没关系.
有任何想法吗?
我想到的一种方法,但我不知道怎么做,在`someMethod’中,将参数移动到堆中,执行lambda然后最终删除它(因为调用者在调用它之后不会真正使用它)方法).但不确定这实际上是否真的很糟糕(我只考虑过它,因为这有点像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()(); } }
是的,我知道,我正在使用原始指针…(eeeeevil ….:p),但至少我可以使用ref参数保留方法的签名.
克隆方法是这样的:
template <class T> struct ConcreteEvent : public Event { virtual Event* heapClone() { return new T(*(T*)this); } // .... more stuff. };
解决方法
有不同的方法可以解决您的问题:您可以通过让调用者持有shared_ptr并将共享指针复制到lambda来使调用者知道额外的引用.这解决了生命周期问题,但仍然依赖于调用者不修改对象.您还可以让编译器通过将该函数作为模板为每个派生类生成不同的排队函数.模板的每个模板实例都知道如何复制其特定类型.您已经驳回了这两种解决方案.我还知道另外一种方法,即在您创建堆类副本的派生类中实现的基类中添加虚拟克隆函数.