В рамках функции-члена я хочу временно установить переменную-член в определенное значение.
Затем, когда эта функция вернется, я хочу сбросить эту переменную-член к заданному известному значению.
Чтобы обезопасить себя от исключений и множественных возвратов, я сделал это с помощью простого RAII-подобного класса. Это определено в рамках функции-члена.
void MyClass::MyMemberFunction() {
struct SetBackToFalse {
SetBackToFalse(bool* p): m_p(p) {}
~SetBackToFalse() {*m_p=false;}
private:
bool* m_p;
};
m_theVariableToChange = true;
SetBackToFalse resetFalse( &m_theVariableToChange ); // Will reset the variable to false.
// Function body that may throw.
}
Это кажется настолько очевидным явлением, что мне было интересно, существует ли такой класс шаблонов, делающий это в стандартной библиотеке C ++?
Пока нет (были предложения для этого). Но реализовать универсальный достаточно просто;
struct scope_exit {
std::function<void()> f_;
explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {}
~scope_exit() { if (f_) f_(); }
};
// ...
m_theVariableToChange = true;
scope_exit resetFalse([&m_theVariableToChange]() { m_theVariableToChange = false; });
Для простоты выше я отредактировал конструкторы копирования и перемещения и т.д …
Отмечать их как = delete
сделаем вышеуказанное минимальным решением. В дальнейшем; Перемещение может быть разрешено при желании, но копирование должно быть запрещено.
Более полный scope_exit
будет выглядеть (онлайн демо здесь);
template <typename F>
struct scope_exit {
F f_;
bool run_;
explicit scope_exit(F f) noexcept : f_(std::move(f)), run_(true) {}
scope_exit(scope_exit&& rhs) noexcept : f_((rhs.run_ = false, std::move(rhs.f_))), run_(true) {}
~scope_exit()
{
if (run_)
f_(); // RAII semantics apply, expected not to throw
}
// "in place" construction expected, no default ctor provided either
// also unclear what should be done with the old functor, should it
// be called since it is no longer needed, or not since *this is not
// going out of scope just yet...
scope_exit& operator=(scope_exit&& rhs) = delete;
// to be explicit...
scope_exit(scope_exit const&) = delete;
scope_exit& operator=(scope_exit const&) = delete;
};
template <typename F>
scope_exit<F> make_scope_exit(F&& f) noexcept
{
return scope_exit<F>{ std::forward<F>(f) };
}
Примечания по реализации;
std::function<void()>
может быть использован для стирания типа функтора. std::function<void()>
предлагает исключительные гарантии для конструкторов перемещения, основанные на исключении, специфичном для удерживаемой функции. Образец этой реализации найден Вотnoexcept
более существенная деталь найдена в Предложение C ++throw
это также согласуется со спецификацией C ++ 11 в спецификации исключений по умолчанию для деструктора. Увидеть cppreference, SO Q&, GotW # 47 а также HIC ++Другие реализации могут быть найдены;
Вы могли бы «злоупотреблять» shared_ptr
за это:
m_theVariableToChange = true;
std::shared_ptr<void> resetFalse(nullptr, [&](void*){ m_theVariableToChange = false; });
Если есть опасения по поводу использования void
как параметр шаблона T
Я нашел следующее в стандарте C ++:
20.8.2.2§2:
… Параметр шаблона T для shared_ptr может быть неполного типа.
Это указывает на то, что T
используется только как указатель, поэтому с помощью void
все должно быть в порядке.
Там нет стандартной версии этого.
CppGoreGuidelines Библиотека поддержки (GSL) имеет обобщенную версию этого называется в конце концов но эта библиотека еще не является качественной продукцией. Это определенно рекомендуемая практика.
final_action
объект для экспресс очистки, если нет подходящего дескриптора ресурсаfinally
менее многословен и труднее ошибиться, чем try
/catch
,
void f(int n)
{
void* p = malloc(1, n);
auto _ = finally([p] { free(p); });
// ...
}
finally
не так грязно, как try
/catch
, но это все еще ad-hoc.
Предпочитаю правильные объекты управления ресурсами.
Подобный вопрос: Самый простой и удобный C ++ 11 ScopeGuard
В этом потоке описана похожая защита для вызова произвольной функции. Чтобы решить вашу проблему, вызовите лямбду, которая сбрасывает вашу переменную.
Например, решение от этот ответ для вашего кода будет:
scope_guard guard1 = [&]{ m_theVariableToChange = false; };
Другой ответ на этот поток отмечает, что аналогичная концепция была предложена для стандартизации C ++ 17; и также представлено решение C ++ 03.