c++ - 使用 C++ lambda 正确实现 finally block

标签 c++ c++11 finally

我想在我的 C++ 程序中实现一个 finally block ,如果不是 native 工具,该语言当然有工具可以做到这一点。我想知道最好的方法是什么?

最佳答案

这个简单的实现似乎是 100% 安全的。

template< typename t >
class sentry {
    t o;
public:
    sentry( t in_o ) : o( std::move( in_o ) ) {}

    sentry( sentry && ) = delete;
    sentry( sentry const & ) = delete;

    ~ sentry() noexcept {
        static_assert( noexcept( o() ),
            "Please check that the finally block cannot throw, "
            "and mark the lambda as noexcept." );
        o();
    }
};

template< typename t >
sentry< t > finally( t o ) { return { std::move( o ) }; }

noexcept 很重要,因为您不想在函数因异常而退出时抛出异常。 (这会导致立即终止。)C++ 不会检查 lambda 是否真的不能抛出任何东西。您手动检查并标记它noexcept。 (见下文。)

工厂函数是必要的,否则无法获得依赖于 lambda 的类型。

必须删除复制和移动构造函数,因为它们可用于隐式生成一个临时对象,该对象将实现另一个哨兵,该哨兵将在销毁时过早调用该 block 。但是默认分配运算符保持不变,因为如果您已经有两个执行不同操作的哨兵,则可以分配它们。 (有点理论,但无论如何。)

如果构造函数是 explicit 会很好,但这似乎排除了返回值的就地初始化。由于该类是不可移动的,因此位于调用者范围内的对象必须由 return 语句中的表达式直接初始化。

要使用,只需像这样定义一个守卫:

auto && working_state_guard = finally( [&]() noexcept {
    reset_working_state();
} );

绑定(bind)到引用是必不可少的,因为在调用范围内声明一个真实对象需要从函数返回值移动初始化该对象。

大约 4.7 版,g++ -Wall 会给出未使用防护的警告。无论您是否针对它进行编码,您都可以在函数末尾添加一些安全性和文档,并使用成语:

static_cast< void >( working_state_guard );

这让读者从作用域的开始就知道代码的执行情况,并且可以提醒在复制粘贴代码时仔细检查。


用法。

int main() {
    auto && guard = finally( []() noexcept {
        try {
            std::cout << "Goodbye!\n";
        } catch ( ... ) {
            // Throwing an exception from here would be worse than *anything*.
        }
    } );

    std::cin.exceptions( std::ios::failbit );
    try {
        float age;
        std::cout << "How old are you?\n";
        std::cin >> age;
        std::cout << "You are " << age << " years (or whatever) old\n";
    } catch ( std::ios::failure & ) {
        std::cout << "Sorry, didn't understand that.\n";
        throw;
    }
    static_cast< void >( guard );
}

这会产生类似的输出

$ ./sentry 
How old are you?
3
You are 3 years (or whatever) old.
Goodbye!
$ ./sentry 
How old are you?
four
Sorry, didn't understand that.
Goodbye!
terminate called after throwing an instance of 'std::ios_base::failure'
  what():  basic_ios::clear
Abort trap: 6

如何取消正在执行的 Action ?

查看一些“以前的尝试”,我看到了一个事务性 commit() 方法。我认为这不属于 ScopeGuard/finally block 实现。实现一个协议(protocol)是包含的仿函数的责任,所以正确的分工是在其中封装一个 bool 标志,例如通过捕获一个 bool 本地标志,并在事务时翻转该标志完成了。

同样,试图通过重新分配仿函数本身来取消操作只是一种困惑的方法。通常更喜欢现有协议(protocol)中的一个额外案例,而不是围绕旧协议(protocol)发明一个新协议(protocol)。

关于c++ - 使用 C++ lambda 正确实现 finally block ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17356258/

相关文章:

java - Try-catch-finally-return 澄清

ruby - Ruby 中 call/cc 和 "ensure"的语义

c++ - 赋值运算符在我制作类模板时使代码崩溃

c++ - 将 ASCII EOH 字符表示为字符串?

c++ - Eclipse 不会更改 C++ 中的输出类型

c++ - 在 Eclipse Juno ADT 中启用 C++11 符号解析

c++ - 在哪里可以找到有关 C++11、std 新功能和 Tr1 的可靠信息?

c++ - 为什么不能在三元运算符中使用braced-init-list?

最终好奇的Java未初始化变量

c++ - ID3D11Device::CreateBuffer 在幕后做了什么?