2015-08-10 2 views
0

Позволяет спросить об этом простом области действия охранником: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() будет вызываться до лямбда-деструктора?

+0

ли это компилировать? Вы назначаете временную ссылку на изменяемое значение l-value. –

+0

Он компилируется. Во-первых, я инициализирую переменную 'FIN ## l ## clo' со значением закрытия. (возможно, копия elision помогает здесь немного). Затем присвойте ссылку 'FIN ## l ## clo' в поле' t' переменной 'FIN ## l ## fin'. –

+0

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

ответ

2

Да, это безопасно. Макрос сохраняет лямбда в локальной переменной. Порядок уничтожения локальных переменных фиксирован (в порядке, обратном порядку). Таким образом, гарантируется, что деструктор ~finop_t() вызывается перед соответствующим дембратором лямбда (FIN ## l ## clo).

2

Уничтожение локальных переменных происходит в обратном порядке их построения.

И вот более эффективный способ, который не нуждается в ссылках и использует копирование для построения лямбда на месте.

(обратите внимание, вы можете рассмотреть [&], а не [=], но это вам судить)

#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; 
} 
+0

Это почти то же, что и моя версия http://stackoverflow.com/questions/31922693/c-why-this-simple-scope-guard-works Но я думаю, что нехорошо полагаться на копирование элиции внутри деструктора (но может быть, это так). –

+0

О, спасибо, что указали на '[&]' !!! это действительно имеет смысл. –

+0

Во время деструктора отсутствует копия. Это происходит во время вызова 'make_finop_t()' и как RVO (оптимизация возвращаемого значения) при возврате из 'make_finop_t()' - C++ 11 дает нам совершенную пересылку полностью. –