golang-style" defer"在C ++中

时间:2015-10-10 05:23:17

标签: c++ c++11 go functional-programming destructor

我正在阅读go语言的defer声明。它允许您指定函数结束时要执行的操作。例如,如果你有一个文件指针或资源,而不是用每个可能的返回路径写free / delete,你只需要指定一次defer函数。

看起来模拟可能最终会出现在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!

3 个答案:

答案 0 :(得分:15)

Boost在智能指针编程技术中讨论这个问题:

你可以做,例如:

#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 Guidelineshave a guideline使用了gsl::finally函数,其中有一个实现here

有很多代码库为此采用类似的解决方案,因此, 需要这个工具。

相关SO讨论:

答案 1 :(得分:3)

这已经存在,它被称为范围保护。看到这个精彩的演讲:https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C。这使您可以轻松创建在退出时调用的任意可调用对象。这是较新的版本;它是在存在之前很久才开发出来的。

它总体上运作得很好,但我不确定处理异常的意思。从必须在作用域出口处调用的函数抛出异常是一团糟。原因是:抛出异常(并没有立即捕获)时,当前作用域退出。所有析构函数都会运行,异常将继续传播。如果其中一个析构者抛出,你会怎么做?您现在有两个实时例外。

我想有一种语言可以试图解决这个问题,但它非常复杂。在C ++中,投掷析构函数被认为是一个好主意是非常罕见的。

答案 2 :(得分:-3)

这已经存在于C ++中,这是一个非常糟糕的想法,你给出的例子说明了为什么这是一个毫无意义的事情,我希望委员会永远不会介绍它。

例如,如果你有一个文件句柄,那么写一个类来为你做,然后你就不必为每个用例编写一个defer语句,你很容易忘记这样做。或者只是简单地弄错了。你写了一个析构函数,一次。而已。然后你就可以保证这个班级的所有用途都是安全的。它更安全,更容易。