C ++ lambda: how to avoid link slicing if fixed by value

I have a method that takes a parameter that is a reference to the base class, and I call the method body calls, completing the method implementation in queue<function<void()>>

The problem is that I was hoping to lock the method parameter by value so that each lambda in the queue could execute its own copy.

But if I commit by value, then the lambda copy of the reference parameter seems to cut it off, leaving me with a copy of the base class instead of the actual derived class in the link.

If I take the parameter by reference instead, I get the actual derived class in lambda, but obj may go out of scope between method calls or its state may change.

Remember that a method must be reentrant, but not asynchronous or parallel.

This is an example of what I mean (omitting the queue):

 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(); } 

The output when calling the method is as follows:

 DerivedObj obj{}; someMethod(obj); 

There is:

 obj type:10DerivedObj refLambda::obj type:10DerivedObj valLambda::obj type:7BaseObj 

Currently, the only way I was able to save the derived type in method calls is:

  • passing the selected heap of the object from the calling code.
  • capture by reference in lambda.
  • make sure you do not change the source code in the calling code.
  • finally removing a bunch of obj after the method returns.

Like this:

  DerivedObj* obj = new DerivedObj(); someMethod(*obj); delete obj; 

But I was hoping I could just pass the link from the stack of the calling code and it would be fine even if something happened inside someMethod that caused another call to someMethod .

Any ideas?

One of the approaches that I was thinking about, but I'm not sure how to do it, inside `someMethod ', moving the parameter to the heap, executing the lambda and then finally deleting it (since the caller doesn't actually use it after calling this method). But not sure if this is really hacked (I just thought about it because it is a bit like what Objective-C blocks do).

update:

This is the solution I have so far:

 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()(); } } 

Yes, I know, I use raw pointers ... (eeeeevil ....: p), but at least I can save the method signature with the ref parameter.

The clone method is located line by line:

 template <class T> struct ConcreteEvent : public Event { virtual Event* heapClone() { return new T(*(T*)this); } // .... more stuff. }; 
+7
c ++ lambda c ++ 11 c ++ 14
source share
2 answers

It is impossible to achieve the desired result without any intrusive changes. Currently, you have a caller that modifies or destroys its object without worrying about whether the link is in the queue. With this type of caller, your only choice is to make a copy. The function that creates the lambda does not know what type of object you are going to pass, so it does not know how to copy it.

There are different ways to solve your problem: you can make the calling information about the sitelink by holding it shared_ptr and copying the shared pointer into lambda. This solves the problem of life expectancy, but it still depends on the caller, so as not to modify the object. You could also force the compiler to generate different queue functions for each derived class, making this function a template. Each instance of a template template knows how to copy its specific type. You have already rejected both of these decisions. There is only one other approach that I know of by adding a virtual clone function to your base class, which you implement in derived classes to create a copy of the heap.

0
source share

Use a pointer like someMethod instead:

 void someMethod(BaseObj* obj) { std::cout << "\nobj type:" << typeid(*obj).name(); auto refLambda = [&] { std::cout << "\nrefLambda::obj type:" << typeid(*obj).name(); }; auto valLambda = [=] { std::cout << "\nvalLambda::obj type:" << typeid(*obj).name(); }; refLambda(); valLambda(); } int main() { DerivedObj obj; someMethod(&obj); } 

Tested in VS2013, it will print:

 obj type:struct DerivedObj refLambda::obj type:struct DerivedObj valLambda::obj type:struct DerivedObj 
-2
source share

All Articles