C++的五郎式"defer"

golang-style "defer" in C++

本文关键字:defer C++      更新时间:2023-10-16

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

(c++中标准的延迟/终结器实现是什么?作用域保护/作用域退出习惯用法是否会标准化?)在此之前,对析构函数进行回调的对象执行此操作有什么不可预见的吗?看起来局部变量的析构函数顺序是合理的,并且它也可以很好地处理异常,尽管可能不会在信号上退出。

这是一个示例实现…有什么问题吗?

#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!

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

  • http://www.boost.org/doc/libs/1_59_0/libs/smart_ptr/sp_techniques.html处理

你可以这样做,例如:

#include <memory>
#include <iostream>
#include <functional>
using namespace std;
using defer = shared_ptr<void>;    
int main() {
    defer _(nullptr, bind([]{ cout << ", World!"; }));
    cout << "Hello";
}

Or,不含bind:

#include <memory>
#include <iostream>
using namespace std;
using defer = shared_ptr<void>;    
int main() {
    defer _(nullptr, [](...){ cout << ", World!"; });
    cout << "Hello";
}

您也可以为此推出自己的小类,或者使用N3830/P0052的参考实现:

  • N3830: https://github.com/alsliahona/N3830
  • P0052: https://github.com/PeterSommerlad/scope17

c++核心指南也有一个使用gsl::finally函数的指南,这里有一个实现。

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

相关SO讨论:

  • 是否有合适的'所有权-在一个包'& # 39;处理# 39;可用吗?
  • 正确的(资源处理)零规则在哪里?

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

它在一般情况下工作得很好,但我不确定你说它处理异常是什么意思。从必须在作用域出口调用的函数中抛出异常会造成混乱。原因是:当抛出异常(而不是立即捕获)时,当前作用域退出。所有析构函数都将运行,异常将继续传播。如果其中一个析构函数抛出,该怎么办?现在有两个活动异常。

我想有一些语言可以尝试处理这个问题,但是它非常复杂。在c++中,很少会认为抛出析构函数是个好主意。

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

例如,如果你有一个文件句柄,那么就写一个类来为你做这件事,这样你就不必为每个用例写一个defer语句,因为你很容易忘记这样做。或者只是搞错了。只写一次析构函数。就是这样。这样就可以保证类的所有使用都是安全的。这样更安全,更方便。