44

我正在尝试编写一个基于 Alexandrescu 概念但使用 c++11 习语的简单 ScopeGuard。

namespace RAII
{
    template< typename Lambda >
    class ScopeGuard
    {
        mutable bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}

            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
            {
                _al();
            }

            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() const { committed = true; }
    };

    template< typename aLambda , typename rLambda>
    const ScopeGuard< rLambda >& makeScopeGuard( const aLambda& _a , const rLambda& _r)
    {
        return ScopeGuard< rLambda >( _a , _r );
    }

    template<typename rLambda>
    const ScopeGuard< rLambda >& makeScopeGuard(const rLambda& _r)
    {
        return ScopeGuard< rLambda >(_r );
    }
}

这是用法:

void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions() 
{
   std::vector<int> myVec;
   std::vector<int> someOtherVec;

   myVec.push_back(5);
   //first constructor, adquire happens elsewhere
   const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } );  

   //sintactically neater, since everything happens in a single line
   const auto& b = RAII::makeScopeGuard( [&]() { someOtherVec.push_back(42); }
                     , [&]() { someOtherVec.pop_back(); } ); 

   b.commit();
   a.commit();
}

由于我的版本比那里的大多数示例(如 Boost ScopeExit)短得多,我想知道我遗漏了哪些专业。希望我在这里处于 80/20 的场景中(我得到了 80% 的整洁度和 20% 的代码行),但我不禁想知道我是否遗漏了一些重要的东西,或者是否有一些缺点值得提到这个版本的 ScopeGuard 成语

谢谢!

编辑我注意到 makeScopeGuard 的一个非常重要的问题,它在构造函数中采用了 adquire lambda。如果 adquire lambda 抛出,则永远不会调用 release lambda,因为范围保护从未完全构造。在许多情况下,这是所需的行为,但我觉得有时也需要一个在抛出发生时调用回滚的版本:

//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
    return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}

template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
    auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
    _a();
    return scope;
}

所以为了完整起见,我想把完整的代码放在这里,包括测试:


#include <vector>

namespace RAII
{

    template< typename Lambda >
    class ScopeGuard
    {
        bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}

            ScopeGuard( const ScopeGuard& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda) 
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            ScopeGuard( ScopeGuard&& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda)
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
            {
               std::forward<AdquireLambda>(_al)();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda, typename L >
            ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
            {
                std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
            }


            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() { committed = true; }
    };


    //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
    {
        return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
    }

    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
    {
        auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
        _a();
        return scope;
    }

    template<typename rLambda>
    ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
    {
        return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
    }

    namespace basic_usage
    {
        struct Test
        {

            std::vector<int> myVec;
            std::vector<int> someOtherVec;
            bool shouldThrow;
            void run()
            {
                shouldThrow = true;
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
                }
                shouldThrow = false;
                SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                AssertMsg( myVec.size() == 1 && someOtherVec.size() == 1 , "unexpected end state");
                shouldThrow = true;
                myVec.clear(); someOtherVec.clear();  
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
                }
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw()
            {

                myVec.push_back(42);
                auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );  

                auto b = RAII::makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); }
                                    , [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );

                if (shouldThrow) throw 1; 

                b.commit();
                a.commit();
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows() //throw()
            {
                myVec.push_back(42);
                auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );  

                auto b = RAII::makeScopeGuardThatDoesRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); if (shouldThrow) throw 1; }
                                    , [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );

                b.commit();
                a.commit();
            }
        };
    }
}
4

14 回答 14

34

更短:我不知道你们为什么坚持将模板放在警卫班上。

#include <functional>

class scope_guard {
public: 
    template<class Callable> 
    scope_guard(Callable && undo_func) try : f(std::forward<Callable>(undo_func)) {
    } catch(...) {
        undo_func();
        throw;
    }

    scope_guard(scope_guard && other) : f(std::move(other.f)) {
        other.f = nullptr;
    }

    ~scope_guard() {
        if(f) f(); // must not throw
    }

    void dismiss() noexcept {
        f = nullptr;
    }

    scope_guard(const scope_guard&) = delete;
    void operator = (const scope_guard&) = delete;

private:
    std::function<void()> f;
};

请注意,清理代码不抛出是必不可少的,否则您会遇到与抛出析构函数类似的情况。

用法:

// do step 1
step1();
scope_guard guard1 = [&]() {
    // revert step 1
    revert1();
};

// step 2
step2();
guard1.dismiss();

我的灵感来自与 OP 相同的DrDobbs 文章


2017/2018 年编辑:在观看了 André 链接的(部分)Andrei 的演示文稿后(我跳到最后,上面写着“痛苦地接近理想!”)我意识到这是可行的。大多数时候,您不想为所有事情都配备额外的警卫。你只是做一些事情,最后它要么成功,要么应该发生回滚。

2018 年编辑:添加了执行策略,消除了dismiss调用的必要性。

#include <functional>
#include <deque>

class scope_guard {
public:
    enum execution { always, no_exception, exception };

    scope_guard(scope_guard &&) = default;
    explicit scope_guard(execution policy = always) : policy(policy) {}

    template<class Callable>
    scope_guard(Callable && func, execution policy = always) : policy(policy) {
        this->operator += <Callable>(std::forward<Callable>(func));
    }

    template<class Callable>
    scope_guard& operator += (Callable && func) try {
        handlers.emplace_front(std::forward<Callable>(func));
        return *this;
    } catch(...) {
        if(policy != no_exception) func();
        throw;
    }

    ~scope_guard() {
        if(policy == always || (std::uncaught_exception() == (policy == exception))) {
            for(auto &f : handlers) try {
                f(); // must not throw
            } catch(...) { /* std::terminate(); ? */ }
        }
    }

    void dismiss() noexcept {
        handlers.clear();
    }

private:
    scope_guard(const scope_guard&) = delete;
    void operator = (const scope_guard&) = delete;

    std::deque<std::function<void()>> handlers;
    execution policy = always;
};

用法:

scope_guard scope_exit, scope_fail(scope_guard::execution::exception);

action1();
scope_exit += [](){ cleanup1(); };
scope_fail += [](){ rollback1(); };

action2();
scope_exit += [](){ cleanup2(); };
scope_fail += [](){ rollback2(); };

// ...
于 2015-02-09T15:34:56.333 回答
23

Boost.ScopeExit 是一个需要与非 C++11 代码一起工作的宏,即无法访问该语言中的 lambda 的代码。它使用了一些巧妙的模板技巧(比如滥用<模板和比较运算符所产生的歧义!)和预处理器来模拟 lambda 功能。这就是代码更长的原因。

显示的代码也有问题(这可能是使用现有解决方案的最强有力的理由):由于返回对临时对象的引用,它会调用未定义的行为。

由于您正在尝试使用 C++11 功能,因此可以通过使用移动语义、右值引用和完美转发来大大改进代码:

template< typename Lambda >
class ScopeGuard
{
    bool committed; // not mutable
    Lambda rollbackLambda; 
    public:


        // make sure this is not a copy ctor
        template <typename L,
                  DisableIf<std::is_same<RemoveReference<RemoveCv<L>>, ScopeGuard<Lambda>>> =_
        >
        /* see http://loungecpp.net/w/EnableIf_in_C%2B%2B11
         * and http://stackoverflow.com/q/10180552/46642 for info on DisableIf
         */
        explicit ScopeGuard(L&& _l)
        // explicit, unless you want implicit conversions from *everything*
        : committed(false)
        , rollbackLambda(std::forward<L>(_l)) // avoid copying unless necessary
        {}

        template< typename AdquireLambda, typename L >
        ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
        {
            std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
        }

        // move constructor
        ScopeGuard(ScopeGuard&& that)
        : committed(that.committed)
        , rollbackLambda(std::move(that.rollbackLambda)) {
            that.committed = true;
        }

        ~ScopeGuard()
        {
            if (!committed)
                rollbackLambda(); // what if this throws?
        }
        void commit() { committed = true; } // no need for const
};

template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuard( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
    return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}

template<typename rLambda>
ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
{
    return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
}
于 2012-04-22T18:27:35.493 回答
15

您可能有兴趣观看Andrei 自己的关于如何使用 c++11 改进 scopedguard 的演示文稿

于 2013-03-08T22:29:51.317 回答
15

您可以将std::unique_ptr其用于实现 RAII 模式的目的。例如:

vector<int> v{};
v.push_back(42);
unique_ptr<decltype(v), function<void(decltype(v)*)>>
    p{&v, [] (decltype(v)* v) { if (uncaught_exception()) { v->pop_back(); }}};
throw exception(); // rollback 
p.release(); // explicit commit

如果在异常处于活动状态时离开了范围,则删除器函数会unique_ptr p回滚以前插入的值。如果您更喜欢显式提交,您可以删除删除函数中的问题并在释放指针uncaugth_exception()的块末尾添加。p.release()在此处查看演示

于 2016-06-17T10:35:14.077 回答
13

我使用它就像一个魅力,没有额外的代码。

shared_ptr<int> x(NULL, [&](int *) { CloseResource(); });
于 2018-05-08T18:48:45.090 回答
8

这种方法有可能通过提案P0052R0在 C++17 或 Library Fundamentals TS 中标准化

template <typename EF>
scope_exit<see below> make_scope_exit(EF &&exit_function) noexcept;

template <typename EF>
scope_exit<see below> make_scope_fail(EF && exit_function) noexcept;

template <typename EF>
scope_exit<see below> make_scope_success(EF && exit_function) noexcept;

乍一看,这有同样的警告,std::async因为您必须存储返回值,否则将立即调用析构函数并且它不会按预期工作。

于 2016-01-24T13:42:17.057 回答
8

大多数其他解决方案都涉及movelambda 的 a,例如,通过使用 lambda 参数来初始化 astd::function或从 lambda 推导出的类型的对象。

这是一个非常简单的方法,它允许使用命名的 lambda 而无需移动它(需要 C++17):

template<typename F>
struct OnExit
{
    F func;
    OnExit(F&& f): func(std::forward<F>(f)) {}
    ~OnExit() { func();  }
};

template<typename F> OnExit(F&& frv) -> OnExit<F>;

int main()
{
    auto func = []{ };
    OnExit x(func);       // No move, F& refers to func
    OnExit y([]{});       // Lambda is moved to F.
}

当参数是左值时,推导指南使 F 推导为左值引用。

于 2020-04-16T04:29:41.087 回答
5

没有承诺跟踪,但非常整洁和快速。

template <typename F>
struct ScopeExit {
    ScopeExit(F&& f) : m_f(std::forward<F>(f)) {}
    ~ScopeExit() { m_f(); }
    F m_f;
};

template <typename F>
ScopeExit<F> makeScopeExit(F&& f) {
    return ScopeExit<F>(std::forward<F>(f));
};

#define STRING_JOIN(arg1, arg2) STRING_JOIN2(arg1, arg2)
#define STRING_JOIN2(arg1, arg2) arg1 ## arg2

#define ON_SCOPE_EXIT(code) auto STRING_JOIN(scopeExit, __LINE__) = makeScopeExit([&](){code;})

用法

{
    puts("a");
    auto _ = makeScopeExit([]() { puts("b"); });
    // More readable with a macro
    ON_SCOPE_EXIT(puts("c"));
} # prints a, c, b
于 2017-02-28T10:53:07.140 回答
4

makeScopeGuard 返回一个常量引用。您不能将此 const 引用存储在调用方的 const ref 中,如下所示:

const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } ); 

所以你正在调用未定义的行为。

Herb Sutter GOTW 88提供了一些关于在 const 引用中存储值的背景知识。

于 2012-04-22T18:26:27.180 回答
3

FWIW 我认为 Andrei Alexandrescu 在他的 CppCon 2015 关于“声明式控制流”(视频幻灯片)的演讲中使用了非常简洁的语法。

以下代码深受其启发:

Try It Online GitHub Gist

#include <iostream>
#include <type_traits>
#include <utility>

using std::cout;
using std::endl;

template <typename F>
struct ScopeExitGuard
{
public:
    struct Init
    {
        template <typename G>
        ScopeExitGuard<typename std::remove_reference<G>::type>
        operator+(G&& onScopeExit_)
        {
            return {false, std::forward<G>(onScopeExit_)};
        }
    };

private:
    bool m_callOnScopeExit = false;
    mutable F m_onScopeExit;

public:
    ScopeExitGuard() = delete;
    template <typename G> ScopeExitGuard(const ScopeExitGuard<G>&) = delete;
    template <typename G> void operator=(const ScopeExitGuard<G>&) = delete;
    template <typename G> void operator=(ScopeExitGuard<G>&&) = delete;

    ScopeExitGuard(const bool callOnScopeExit_, F&& onScopeExit_)
    : m_callOnScopeExit(callOnScopeExit_)
    , m_onScopeExit(std::forward<F>(onScopeExit_))
    {}

    template <typename G>
    ScopeExitGuard(ScopeExitGuard<G>&& other)
    : m_callOnScopeExit(true)
    , m_onScopeExit(std::move(other.m_onScopeExit))
    {
        other.m_callOnScopeExit = false;
    }

    ~ScopeExitGuard()
    {
        if (m_callOnScopeExit)
        {
            m_onScopeExit();
        }
    }
};

#define ON_SCOPE_EXIT_GUARD_VAR_2(line_num) _scope_exit_guard_ ## line_num ## _
#define ON_SCOPE_EXIT_GUARD_VAR(line_num) ON_SCOPE_EXIT_GUARD_VAR_2(line_num)
// usage
//     ON_SCOPE_EXIT <callable>
//
// example
//     ON_SCOPE_EXIT [] { cout << "bye" << endl; };
#define ON_SCOPE_EXIT                             \
    const auto ON_SCOPE_EXIT_GUARD_VAR(__LINE__)  \
        = ScopeExitGuard<void*>::Init{} + /* the trailing '+' is the trick to the call syntax ;) */


int main()
{
    ON_SCOPE_EXIT [] {
        cout << "on scope exit 1" << endl;
    };

    ON_SCOPE_EXIT [] {
        cout << "on scope exit 2" << endl;
    };

    cout << "in scope" << endl;  // "in scope"
}
// "on scope exit 2"
// "on scope exit 1"

对于您的用例,您可能还感兴趣std::uncaught_exception()std::uncaught_exceptions()了解您是“正常”退出范围还是在引发异常之后:

ON_SCOPE_EXIT [] {
    if (std::uncaught_exception()) {
        cout << "an exception has been thrown" << endl;
    }
    else {
        cout << "we're probably ok" << endl;
    }
};

高温高压

于 2018-10-12T21:25:41.550 回答
1

这是另一个,现在是@kwarnke 的变体:

std::vector< int > v{ };

v.push_back( 42 );

std::shared_ptr< void > guard( nullptr , [ & v ] ( auto )
{
    v.pop_back( );
} );
于 2017-05-15T21:18:28.823 回答
1

这是我在 C++17 中提出的一个。将其移植到 C++11 和/或添加停用选项很简单:

template<class F>
struct scope_guard
{
    F f_;
    ~scope_guard() { f_(); }
};

template<class F> scope_guard(F) -> scope_guard<F>;

用法:

void foo()
{
    scope_guard sg1{ []{...} };
    auto sg2 = scope_guard{ []{...} };
}

编辑:在同一个键这里是只在“异常”时关闭的警卫:

#include <exception>

template<class F>
struct xguard
{
    F   f_;
    int count_ = std::uncaught_exceptions();

    ~xguard() { if (std::uncaught_exceptions() != count_) f_(); }
};

template<class F> xguard(F) -> xguard<F>;

用法:

void foobar()
{
    xguard xg{ []{...} };
    ...
    // no need to deactivate if everything is good

    xguard{ []{...} },   // will go off only if foo() or bar() throw
        foo(),
        bar();

    // 2nd guard is no longer alive here
}
于 2022-02-16T22:07:03.760 回答
0

你已经选择了一个答案,但无论如何我都会接受挑战:

#include <iostream>
#include <type_traits>
#include <utility>

template < typename RollbackLambda >
class ScopeGuard;

template < typename RollbackLambda >
auto  make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
 std::decay<RollbackLambda>::type>;

template < typename RollbackLambda >
class ScopeGuard
{
    // The input may have any of: cv-qualifiers, l-value reference, or both;
    // so I don't do an exact template match.  I want the return to be just
    // "ScopeGuard," but I can't figure it out right now, so I'll make every
    // version a friend.
    template < typename AnyRollbackLambda >
    friend
    auto make_ScopeGuard( AnyRollbackLambda && ) -> ScopeGuard<typename
     std::decay<AnyRollbackLambda>::type>;

public:
    using lambda_type = RollbackLambda;

private:
    // Keep the lambda, of course, and if you really need it at the end
    bool        committed;
    lambda_type  rollback;

    // Keep the main constructor private so regular creation goes through the
    // external function.
    explicit  ScopeGuard( lambda_type rollback_action )
        : committed{ false }, rollback{ std::move(rollback_action) }
    {}

public:
    // Do allow moves
    ScopeGuard( ScopeGuard &&that )
        : committed{ that.committed }, rollback{ std::move(that.rollback) }
    { that.committed = true; }
    ScopeGuard( ScopeGuard const & ) = delete;

    // Cancel the roll-back from being called.
    void  commit()  { committed = true; }

    // The magic happens in the destructor.
    // (Too bad that there's still no way, AFAIK, to reliably check if you're
    // already in exception-caused stack unwinding.  For now, we just hope the
    // roll-back doesn't throw.)
    ~ScopeGuard()  { if (not committed) rollback(); }
};

template < typename RollbackLambda >
auto  make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
 std::decay<RollbackLambda>::type>
{
    using std::forward;

    return ScopeGuard<typename std::decay<RollbackLambda>::type>{
     forward<RollbackLambda>(r) };
}

template < typename ActionLambda, typename RollbackLambda >
auto  make_ScopeGuard( ActionLambda && a, RollbackLambda &&r, bool
 roll_back_if_action_throws ) -> ScopeGuard<typename
 std::decay<RollbackLambda>::type>
{
    using std::forward;

    if ( not roll_back_if_action_throws )  forward<ActionLambda>(a)();
    auto  result = make_ScopeGuard( forward<RollbackLambda>(r) );
    if ( roll_back_if_action_throws )  forward<ActionLambda>(a)();
    return result;
}

int  main()
{
    auto aa = make_ScopeGuard( []{std::cout << "Woah" << '\n';} );
    int  b = 1;

    try {
     auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, true );
    } catch (...) {}
    std::cout << b++ << '\n';
    try {
     auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, false );
    } catch (...) {}
    std::cout << b++ << '\n';

    return 0;
}
// Should write: "0", "2", and "Woah" in that order on separate lines.

您没有创建函数和构造函数,而是仅限于创建函数,主构造函数是private. 我不知道如何将friend-ed 实例化限制为仅涉及当前模板参数的实例化。(也许是因为该参数仅在返回类型中提及。)也许可以在此站点上询问对此的修复。由于第一个动作不需要存储,它只存在于创建函数中。throw如果从第一个操作触发回滚,则有一个布尔参数来标记。

std::decay部分去除了 cv 限定符和参考标记。但是如果输入类型是内置数组,则不能将其用于此通用目的,因为它也会应用数组到指针的转换。

于 2013-11-11T13:28:44.477 回答
0

还有一个答案,但我担心我发现其他人都缺乏某种方式。值得注意的是,接受的答案可以追溯到 2012 年,但它有一个重要的错误(请参阅此评论)。由此可见测试的重要性。

是一个 >=C++11 scope_guard 的实现,它是公开可用并经过广泛测试的。它的意思是/拥有:

  • 现代、优雅、简单(主要是单功能界面,没有宏)
  • 一般(接受任何尊重先决条件的可调用)
  • 仔细记录
  • 瘦回调包装(没有添加std::function或虚拟表惩罚)
  • 适当的异常规范

另请参阅功能的完整列表

于 2018-08-15T21:51:43.817 回答