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деструктор.

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