【问题标题】:Abusing c++11 unique_ptr to execute code upon leaving the scope离开作用域时滥用 c++11 unique_ptr 执行代码
【发布时间】:2015-04-28 01:38:07
【问题描述】:

我想使用std::unique_ptr 的删除器来保证某些代码会在离开作用域时执行。例如,假设我有一个类 Event,其函数为 set_event()

我想确保在离开作用域时,函数my_event.set_event() 将被调用。我设法得到了类似的东西:

Event my_event;
auto releasing_function = [&my_event] (void*){my_event.set_event();};
std::unique_ptr<void, decltype(releasing_function)> safe_event((void*)1, releasing_function);

但我觉得我们可以做得更好。可能是一个没有这个自动 lambda 函数的衬里,或者避免这个丑陋的(void*)1。甚至可能完全删除unique_ptr

编辑:我想避免使用实用程序类。这太容易了:)

【问题讨论】:

  • 您可以使用类似于BOOST_SCOPE_EXIT 的内容。不过,通常情况下,你会有一个 RAII 类。
  • “太容易了”有什么问题?这比滥用现有的功能(如 unique_ptr)要好,从而将 h**l 从可怜的 schmuck 中弄糊涂,他们必须在未来的某个时候维护它。
  • 实用程序类是正确的方法。编写代码是为了让它尽可能简单,以便在 6 天/周/月/年之内,任何需要维护它的人都知道它在做什么。聪明是坏事。做最简单、最清楚的事情。
  • @RobK:当标准的实用程序类很好时,不需要新的实用程序类。只需要一个小的制造商功能。
  • 真正需要的是 C++ 中的 try / finally。我想知道这是否已经被讨论过:-)

标签: c++ c++11 unique-ptr


【解决方案1】:

在一些头文件中定义一个 maker-function 以便于使用:

template<class F> auto scope_guard(F&& f) {
    return std::unique_ptr<void, std::decay<F>::type>{(void*)1, std::forward<F>(f)};
}

并像这样使用它:

auto unique = scope_guard([&](void*){/* cleanup here */});

【讨论】:

  • auto unique = scope_guard([&amp;]{/* cleanup here */}); 不起作用,它必须是:auto unique = scope_guard([&amp;](void*){/* cleanup here */});(至少对于 c++17 或 c++20)
【解决方案2】:

原来的C++03ScopeGuard by Petru Marginean (and a little Andrei Alexandrescu)使用了各种花样。使用 C++11,您可以使用 std::function 和 lambda 表达式来完成您想要完成的工作。例如

class Scope_guard
{
private:
    function<void()> cleanup_;
public:
    void release() { cleanup_ = []{}; }
    ~Scope_guard() { cleanup_(); }
    Scope_guard( function<void()> f )
        : cleanup_( move( f ) )
    {}
};

免责声明:编译器未触及代码。

注意:显然纯粹主义者更喜欢将其表达为具有工厂函数的类模板,而像我这样的简单主义者(简单人?)更喜欢上面的东西。但你明白了。

【讨论】:

  • clang 倾向于管理好,但是使用 gcc 使用 std::function 有很大的开销,这可以解释为什么人们不愿意。
  • 我更喜欢避免使用实用程序类。 (相应地编辑了我的问题)
  • @MarcGlisse 为什么性能会有差异?两者都将使用类型擦除,从而引发虚拟呼叫。 gcc 有什么不同?
  • 让我添加一个链接:gcc.gnu.org/bugzilla/show_bug.cgi?id=59948,以便稍后阅读此内容的人可以检查它是否仍然正确。 @Pradhan 这只是 gcc 中一些遗漏的优化。
【解决方案3】:

你为什么不“手工”做呢?

{
    struct Guard {
        Event my_event;
        explicit Guard (Event my_event) : my_event (my_event) { }
        ~Guard () { my_event.set_event (); }
    };
    Guard guard {my_event};

    /* ... */

   // when scope is left, guard's destructor will be called
}

【讨论】:

  • 干燥:不要重复自己
  • 我更喜欢避免使用实用程序类。 (相应地编辑了我的问题)
  • @user972014,您是否出于技术原因想要避免使用实用程序类?
  • @RSahu 太简单了,不能表达作者的“聪明”
猜你喜欢
  • 2017-06-15
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多