2015-10-10 2 views
9

Я читал про инструкцию defer. Он позволяет указать действие, которое нужно предпринять, когда функция закончилась. Например, если у вас есть указатель на файл или ресурс, вместо того, чтобы писать бесплатный/удалить с любым возможным обратным путем, вам просто нужно указать функцию отсрочки один раз.golang-style "defer" in C++

Похоже, что аналог может появиться на C++ в конце концов (What is standard defer/finalizer implementation in C++?, Will there be standardization of scope guard/scope exit idioms?). До тех пор, есть ли что-нибудь непредвиденное в том, чтобы делать это с объектом, деструктор которого выполняет обратный вызов? Он выглядит как destructor order for local variables is sane и что он также отлично справляется с исключениями, хотя, возможно, не выходит из сигналов.

Вот пример реализации ... есть ли что-то тревожное?

#include <iostream> 
#include <functional> 
using namespace std; 

class FrameExitTask { 
    std::function<void()> func_; 
public: 
    FrameExitTask(std::function<void()> func) : 
    func_(func) { 
    } 
    ~FrameExitTask() { 
     func_(); 
    } 
    FrameExitTask& operator=(const FrameExitTask&) = delete; 
    FrameExitTask(const FrameExitTask&) = delete; 
}; 

int main() { 
    FrameExitTask outer_task([](){cout << "world!";}); 
    FrameExitTask inner_task([](){cout << "Hello, ";}); 
    if (1+1 == 2) 
     return -1; 
    FrameExitTask skipped_task([](){cout << "Blam";}); 
} 

Выход: Hello, world!

+2

Это может быть более подходящим для [Просмотр Кода] (http://codereview.stackexchange.com). Обратите внимание, что уже существует множество реализаций этого класса ScopeGuard, поэтому зачем изобретать колесо? Некоторые реализации, которые я знаю, имеют различные преимущества или специальные инструменты, которые я бы предпочел для вашей версии (например, никакого стирания типа). – dyp

+2

Проблема в том, что каждый класс должен уже делать то, что вы делаете в своем деструкторе, поэтому варианты использования на C++ намного реже, чем в Go. Если вы ищете существующие реализации, проверьте boost.ScopeExit. Думаю, что у Фолли Фолса тоже есть. – inf

+1

Возможно, вам лучше пометить деструктор 'noexcept'. Если функция, которую вы используете 'FrameExitTask', возвращается нормально, исключение из вашего обработчика finally, вероятно, будет работать. Если функция выходит из-за какого-либо другого исключения, то второе исключение из вашего обработчика вызовет проблемы. –

ответ

3

Это уже существует, и это называется областью охраны. Смотрите этот фантастический разговор: https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C. Это позволяет легко создавать произвольные вызываемые вызовы при выходе. Это более новая версия; он был разработан первоначально задолго до того, как существовал.

Он отлично работает в целом, но я не уверен, что вы подразумеваете под этим, обрабатывая исключения. Выбрасывание исключений из функции, которая должна быть вызвана при выходе из области видимости, - беспорядок. Причина: когда исключение выбрано (и не сразу поймано), текущая область выходит. Все деструкторы запускаются, и исключение будет продолжать распространяться. Если один из деструкторов выбрасывает, что вы делаете? Теперь у вас есть два живых исключения.

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

+0

Я имел в виду, что он правильно работает, когда исключение выбрасывается в середине функции, а не в обратном вызове. Поиск ScopeGuard очень полезен. Благодаря! – daveagp

-1

Это уже существует в C++, и это очень плохая идея, и пример, который вы дали, иллюстрирует, почему это бессмысленно, и я надеюсь, что Комитет его никогда не вводит.

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

7

подталкивания обсудить это в смарт-указатель методов программирования:

можно сделать, например:

#include <memory> 
#include <iostream> 
#include <functional> 

using namespace std; 
using defer = shared_ptr<void>;  

int main() { 
    defer _(nullptr, bind([]{ cout << ", World!"; })); 
    cout << "Hello"; 
} 

Или без bind:

#include <memory> 
#include <iostream> 

using namespace std; 
using defer = shared_ptr<void>;  

int main() { 
    defer _(nullptr, [](...){ cout << ", World!"; }); 
    cout << "Hello"; 
} 

Вы можете также и Свиток свой собственный небольшой класс для таких, или использовать эталонной реализации для N3830/P0052:

The C++ Core Guidelines также have a guideline, в котором используется функция gsl::finally, для которой есть реализация here.

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

Связанные SO обсуждение:

 Смежные вопросы

  • Нет связанных вопросов^_^