C++: еще один простой охранник
Давайте спросим вас об этом простом ограждении:
template <class T>
struct finop_t {
T& t;
~finop_t() { t(); }
};
#define FINALLY__(l, cl) \
auto FIN ## l ## clo = cl; \
finop_t<decltype(FIN ## l ## clo)> FIN ## l ## fin { FIN ## l ## clo}
#define FINALLY_(l, cl) FINALLY__(l, cl)
#define FINALLY(...) FINALLY_(__LINE__, ([=](){__VA_ARGS__}))
int main() {
FINALLY( std::cout << "hello" << std::endl ; );
std::cout << "one" << std::endl;
FINALLY( std::cout << "world" << std::endl ; );
std::cout << "second" << std::endl;
return 0;
}
Здесь безопасно полагаться на порядок уничтожения? то есть безопасно предположить, что ~finop_t()
будет называться раньше лямбда-деструктор?
2 ответа
Разрушение локальных переменных происходит в обратном порядке их построения.
А вот более эффективный способ, который не требует ссылок и использует copy-elision для создания лямбда-выражения на месте.
(обратите внимание, вы можете рассмотреть [&], а не [=], но это вам решать)
#include <iostream>
template <class T>
struct finop_t {
finop_t(T&& t) : t(std::forward<T>(t)) {}
T t;
~finop_t() { t(); }
};
template<class F>
finop_t<F> make_finop_t(F&& f)
{
return finop_t<F>(std::forward<F>(f));
}
#define FINALLY__(l, cl) \
auto FIN ## l ## fin = make_finop_t(cl);
#define FINALLY_(l, cl) FINALLY__(l, cl)
#define FINALLY(...) FINALLY_(__LINE__, ([=](){__VA_ARGS__}))
int main() {
FINALLY( std::cout << "hello" << std::endl ; );
std::cout << "one" << std::endl;
FINALLY( std::cout << "world" << std::endl ; );
std::cout << "second" << std::endl;
return 0;
}
Да, это безопасно. Макрос хранит лямбду в локальной переменной. Порядок уничтожения локальных переменных фиксирован (в обратном порядке построения). Таким образом, гарантируется, что ~finop_t()
деструктор вызывается перед соответствующей лямбда (FIN ## l ## clo
деструктор.