是否有 C++ 标准 class 将变量设置为范围退出时的值

Is there a C++ standard class to set a variable to a value at scope exit

在一个成员函数的范围内,我想临时设置一个成员变量为某个值。

那么,当这个函数returns的时候,我想把这个成员变量重新设置为一个给定的已知值

为了防止异常和多重 returns,我已经使用像 class 这样的简单 RAII 来完成它。它是在成员函数的范围内定义的。

void MyClass::MyMemberFunction() {
    struct SetBackToFalse {
        SetBackToFalse(bool* p): m_p(p) {}
        ~SetBackToFalse() {*m_p=false;}
    private:
        bool* m_p;
    };

    m_theVariableToChange = true;
    SetBackToFalse resetFalse( &m_theVariableToChange ); // Will reset the variable to false.

    // Function body that may throw.
}

这看起来很平常,我想知道 C++ 标准库中是否有这样的模板 class 这样做?

还没有(已经有这方面的提议)。但是实现一个通用的很简单;

struct scope_exit {
  std::function<void()> f_;
  explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {}
  ~scope_exit() { if (f_) f_(); }
};
// ...
m_theVariableToChange = true;
scope_exit resetFalse([&m_theVariableToChange]() { m_theVariableToChange = false; });

为简单起见,我编辑了复制和移动构造函数等...

将它们标记为 = delete 将使上述成为最小的解决方案。进一步;如果需要,可以允许移动,但应该禁止复制。


更完整的 scope_exit 看起来像 (online demo here);

template <typename F>
struct scope_exit {
  F f_;
  bool run_;
  explicit scope_exit(F f) noexcept : f_(std::move(f)), run_(true) {}
  scope_exit(scope_exit&& rhs) noexcept : f_((rhs.run_ = false, std::move(rhs.f_))), run_(true) {}
  ~scope_exit()
  {
    if (run_)
      f_(); // RAII semantics apply, expected not to throw
  }

  // "in place" construction expected, no default ctor provided either
  // also unclear what should be done with the old functor, should it
  // be called since it is no longer needed, or not since *this is not
  // going out of scope just yet...
  scope_exit& operator=(scope_exit&& rhs) = delete;
  // to be explicit...
  scope_exit(scope_exit const&) = delete;
  scope_exit& operator=(scope_exit const&) = delete;
};

template <typename F>
scope_exit<F> make_scope_exit(F&& f) noexcept
{
  return scope_exit<F>{ std::forward<F>(f) };
}

实施注意事项;

  • std::function<void()>可用于擦除仿函数的类型。 std::function<void()> 根据持有函数的特定异常为移动构造函数提供异常保证。已找到此实现的示例 here
  • 这些异常规范与 C++ 提议和 GSL 实现一致
  • 我已经编辑了 noexcept 的大部分动机,更多详细信息可以在 C++ proposal
  • 中找到
  • 析构函数的"usual"RAII语义,因此"scope exit"函数适用;它不会 throw,这也符合 C++11 规范关于析构函数的默认异常规范。参见 cppreference, SO Q&A, GotW#47 and HIC++

可以找到其他实现;

您可以 'abuse' shared_ptr 为此:

m_theVariableToChange = true;
std::shared_ptr<void> resetFalse(nullptr, [&](void*){ m_theVariableToChange = false; });

如果担心使用 void 作为模板参数 T,我在 C++ 标准中发现了以下内容:

20.8.2.2§2:

... The template parameter T of shared_ptr may be an incomplete type.

这表明T仅用作指针,因此使用void应该没问题。

没有标准版本。

CppGoreGuidelines Support Library (GSL) has a generalized version of this called finally 但是那个库还不是生产质量。绝对推荐的做法。

E.19:如果没有合适的资源句柄可用,使用final_action对象表示清理

原因

finallytry/catch.

更简洁,更不容易出错

例子

void f(int n)
{
    void* p = malloc(1, n);
    auto _ = finally([p] { free(p); });
    // ...
}

备注

finally不像try/catch那么乱,但它仍然是临时的。 优先选择合适的资源管理对象。

类似问题:The simplest and neatest c++11 ScopeGuard

在该线程上描述了一个用于调用任意函数的类似守卫。要解决您的问题,请调用一个 lambda 来重置您的变量。

例如,this answer 中针对您的代码的解决方案是:

scope_guard guard1 = [&]{ m_theVariableToChange = false; };

该线程的另一个答案指出,已为 C++17 标准化提出了类似的概念;并且还提供了一个 C++03 解决方案。