Сами лямбды C++11 подвергаются автоматическому освобождению RAII?

Я хотел бы написать метод класса, который по выбору принимает лямбду, чтобы настроить его поведение. Поэтому при использовании этого класса мне интересно, нужно ли мне беспокоиться о том, что сама лямбда выходит из области видимости?

Лямбда не будет ссылаться ни на какие внешние переменные, поэтому меня не волнует область действия переменных, а только область действия самой лямбды, на которую я буду хранить ссылку в классе.

Нужно ли беспокоиться о том, как / где была создана сама лямбда?

2 ответа

Ссылки, за исключением узких ситуаций, не продлевают срок службы того, на что вы ссылаетесь.

Использование ссылки на то, чье время жизни истекло, является неопределенным поведением.

Неопределенное поведение лямбды без гражданства может быть "я даже не использую свой this указатель ", так что вы можете быть в порядке. Однако, если вы знаете, что лямбда будет без сохранения состояния, вы могли бы вместо этого сохранить указатель на функцию.

Теперь ваш класс, для хранения реальной лямбды, должен быть настроен на этот лямбда-тип. И если это лямбда без гражданства, она почти наверняка будет такой же маленькой (или меньшей), как ссылка на эту лямбду. Так почему бы просто не сохранить копию лямбды?

Если вместо этого вы храните std::function<void()> или тому подобное, это не ссылка на лямбду. Это объект стирания типа, который упаковывает копию лямбды. Хранение ссылки на std::function<void()> после того, как он выйдет из области видимости, это будет плохой идеей, так как он не будет сохранять состояние и отключится и прочитает мусорную память, когда вы попытаетесь вызвать его.

Может быть, не полностью заполнить ваш ответ, но посмотрите, как травяной саттер решил аналогичный RAII с решением лямбды. Смотрите также этот вопрос

template <class T> class locker {
private:
  mutable T m_t; // Copies the lambda here.
  mutable std::mutex m_m;
public:
  locker( T t = T{} ) : m_t(t) {}
  template <typename F>
  auto operator()(F f) const -> decltype(f(m_t)) {
    std::lock_guard<mutex> _{m_m};
    return f(t);
  }
};


// usage 
locker<std::string> s;
s([](string &s) {
  s += "foobar";
  s += "barfoo";
});

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

Другие вопросы по тегам