Подтвердить что ты не робот

Передача и сохранение лямбда-функции в качестве обратных вызовов

Мне было интересно, будет ли это приемлемым подходом к написанию обратных вызовов:

Сохранение обратных вызовов:

struct EventHolder {
    std::function<void()> Callback;
    EventTypes::EventType Type;
};
std::vector<Events::EventHolder> EventCallbacks;

Определение метода:

void On(EventType OnEventType,std::function<void()>&& Callback)
{
    Events::EventHolder NewEvent;
    NewEvent.Callback=std::move(Callback);
    NewEvent.Type=OnEventType;
    EventCallbacks.push_back(std::move(NewEvent));
}

Событие привязки:

Button->On(EventType::Click,[]{
    // ... callback body
});

Мой самый большой вопрос касался передачи обратного вызова по значению. Является ли этот подход действительным?

4b9b3361

Ответ 1

Это совершенно правильный подход к хранению обработчиков событий.

Однако я хотел бы указать некоторые подробности относительно подписи вашей функции для добавления обратного вызова. Вы беспокоитесь о передаче его по значению, а не по ссылке. В вашем примере у вас есть:

void On(EventType OnEventType,std::function<void()>&& Callback)

Это хорошо, если вы будете связывать это только с rvalues. Однако, если нет особых причин, по которым вы хотите запретить это, я бы рекомендовал, чтобы у вас всегда был метод, который принимает параметры по значению или ссылочной ссылке lvalue и добавляет ссылочную версию rvalue в качестве дополнения, если это будет сочтено необходимым.

Не имея метода, который принимает ссылку на lvalue, ваш код в настоящее время не будет скомпилирован, учитывая это:

std::function<void()> func([](){/*something clever*/});
// Do something necessary with func, perhaps logging or debug prints.
Button->On(EventType::Click, func);

Для простоты, когда вы выбираете способ передачи значения, вы можете просто следовать этим рекомендациям в целом:

  • Если вам нужна копия или намеревается изменить отправленное значение, не требуя изменения фактического переданного объекта: перейдите по значению.
  • Если вы намерены изменить отправленное значение и хотите, чтобы эти изменения влияли на переданный фактический объект: pass by reference.
  • Если вы не хотите изменять переданный объект, но считайте полезным избегать копирования: pass by const reference.
  • Если вы берете параметры по значению, ссылке или const-ссылке и считаете, что есть ценные оптимизации, которые могут быть достигнуты с использованием знания о том, что входной параметр является временным: также разрешить передачу по ссылке rvalue.

Ответ 2

Да. Функции представляют собой либо сырые указатели функций, либо классы легкого веса, что их конструктор копирования не имеет побочного эффекта, поэтому их копия должна действовать как исходный объект, поэтому этот подход полностью в порядке. Но почему вы передаете объект по значению, а затем переместите его в исходный контейнер, вы можете передать ссылку, а затем скопировать ее в свой контейнер и перегрузить функцию, которая принимает ссылку на r-значение (не должно быть так важно).