У меня есть метод, который принимает параметр, который является ссылкой на базовый класс, и я епдиеий вызовы тела метода путем обертывания реализации методы в queue<function<void()>>
C++ лямбды: как избежать нарезок ссылки, если захвачено значением
Проблема в том, что я надеялся зафиксировать параметр метода по значению, чтобы каждая лямбда в очереди могла выполнить свою собственную копию.
Но если я фиксирую по значению, то лямбда-копия ссылочного параметра, кажется, срезает его, оставляя меня с копией базового класса вместо фактического производного класса в ссылке.
Если я беру параметр по ссылке вместо этого, я получаю фактический производный класс в лямбда, но 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);
Is:
obj type:10DerivedObj
refLambda::obj type:10DerivedObj
valLambda::obj type:7BaseObj
На данный момент единственным способом, которым я смог сохранить производный тип в вызовах метода, является:
- Передача кучи выделенного объекта из вызывающего кода.
- захват по ссылке в лямбда.
- убедитесь, что вы не изменяете оригинал в вызывающем коде.
- , наконец, удаляет кучу obj после возврата метода.
Как это:
DerivedObj* obj = new DerivedObj();
someMethod(*obj);
delete obj;
Но я надеялся, чтобы иметь возможность просто передать ссылку из стека вызова кода и будет хорошо, даже если внутри 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()();
}
}
Да, я знаю, я использую сырые указатели ... (eeeeevil ....: р), но по крайней мере я могу сохранить подпись метода с параметром ref.
Метод клонирования вдоль линий этого:
template <class T>
struct ConcreteEvent : public Event {
virtual Event* heapClone() {
return new T(*(T*)this);
}
// .... more stuff.
};
как насчет make 'someMethod' метод шаблона? Вы не можете копировать объект без его типа. или просто передайте 'shared_ptr' около –
@BryanChen умный указатель будет работать, но я пытался его избежать. То же, что и методы шаблонов. – SaldaVonSchwartz
Обратите внимание на [Основные рекомендации C++] (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md). Я считаю, что код C++, помеченный C++ 11 или C++ 14, должен серьезно рассмотреть возможность использования умного указателя вместо голого нового и удаления. –