在 C++0x 中模拟 finally 块

发布于 2024-11-10 07:59:27 字数 2008 浏览 0 评论 0原文

受到另一个主题的启发,我写了此代码模拟 finally 块:

#include <cassert>
#include <iostream>

struct base { virtual ~base(){} };

template<typename TLambda>
struct exec : base 
{
   TLambda lambda;
   exec(TLambda l) : lambda(l){}
   ~exec() { lambda(); }
};

class lambda{
    base *pbase;
public:
    template<typename TLambda>
    lambda(TLambda l): pbase(new exec<TLambda>(l)){}
    ~lambda() { delete pbase; }
};

class A{
    int a;
public:
    void start(){
        int a=1;        
        lambda finally = [&]{a=2; std::cout<<"finally executed";}; 
        try{
            assert(a==1);
            //do stuff
        }
        catch(int){
            //do stuff
        }
    }
};
int main() {
    A a;
    a.start();
}

输出 (ideone):

finally executed

@Johannes似乎认为它并不完全正确,并且 评论说

如果编译器不这样做,它可能会崩溃 删除副本中的临时内容 初始化,因为然后它 使用同一指针删除两次 值

我想知道具体 是多少。帮助我理解问题:-)


编辑:

问题修复为:

class lambda{
    base *pbase;
public:
    template<typename TLambda>
    lambda(TLambda l): pbase(new exec<TLambda>(l)){}
    ~lambda() { delete pbase; }

    lambda(const lambda&)= delete;            //disable copy ctor
    lambda& operator=(const lambda&)= delete; //disable copy assignment
};

然后将其用作:

//direct initialization, no copy-initialization
lambda finally([&]{a=2;  std::cout << "finally executed" << std::endl; }); 

完整代码: http:// /www.ideone.com/hsX0X

Inspired from the other topic, I wrote this code which simulates a finally block:

#include <cassert>
#include <iostream>

struct base { virtual ~base(){} };

template<typename TLambda>
struct exec : base 
{
   TLambda lambda;
   exec(TLambda l) : lambda(l){}
   ~exec() { lambda(); }
};

class lambda{
    base *pbase;
public:
    template<typename TLambda>
    lambda(TLambda l): pbase(new exec<TLambda>(l)){}
    ~lambda() { delete pbase; }
};

class A{
    int a;
public:
    void start(){
        int a=1;        
        lambda finally = [&]{a=2; std::cout<<"finally executed";}; 
        try{
            assert(a==1);
            //do stuff
        }
        catch(int){
            //do stuff
        }
    }
};
int main() {
    A a;
    a.start();
}

Output (ideone):

finally executed

@Johannes seems to think that its not entirely correct, and commented that:

It can crash if the compiler doesn't
elide the temporary in the copy
initialization, because then it
deletes twice with the same pointer
value

I would like to know how exactly. Help me understanding the problem :-)


EDIT:

Problem fixed as:

class lambda{
    base *pbase;
public:
    template<typename TLambda>
    lambda(TLambda l): pbase(new exec<TLambda>(l)){}
    ~lambda() { delete pbase; }

    lambda(const lambda&)= delete;            //disable copy ctor
    lambda& operator=(const lambda&)= delete; //disable copy assignment
};

And then use it as:

//direct initialization, no copy-initialization
lambda finally([&]{a=2;  std::cout << "finally executed" << std::endl; }); 

Complete code : http://www.ideone.com/hsX0X

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

原来是傀儡 2024-11-17 07:59:27

在此初始化中:

lambda finally = [&]{a=2; std::cout<<"finally executed";};

可以使用 lambda 隐式定义的复制构造函数。这只会复制原始指针pbase,然后该指针将被多次删除。

例如

$ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors lambdafun.cc 
$ ./a.out 
a.out: lambdafun.cc:29: void A::start(): Assertion `a==1' failed.
finally executedAborted (core dumped)

,实际上,您的断言触发掩盖了双重删除问题,但这证明了我强调的崩溃。

$ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors -DNDEBUG lambdafun.cc 
$ ./a.out 
Segmentation fault (core dumped)

In this initialization:

lambda finally = [&]{a=2; std::cout<<"finally executed";};

The implicitly defined copy constructor for lambda may be used. This will just copy the raw pointer pbase which will then be deleted more than once.

E.g.

$ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors lambdafun.cc 
$ ./a.out 
a.out: lambdafun.cc:29: void A::start(): Assertion `a==1' failed.
finally executedAborted (core dumped)

Actually, your assert firing masks the double delete problem, but this demonstrates the crash I was highlighting.

$ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors -DNDEBUG lambdafun.cc 
$ ./a.out 
Segmentation fault (core dumped)
萌逼全场 2024-11-17 07:59:27

看起来比必要的要复杂得多。为什么不只是:

class finally
{
    std::function<void (void)> const action;
    finally(const finally&) = delete;

public:
    finally(std::function<void (void)> a)
        : action(a)
    {}

    ~finally() { action(); }
};

但总的来说,人们应该尽量不要将不良的 Java 习惯带入 C++ 中。

Seems way more complicated than necessary. Why not just:

class finally
{
    std::function<void (void)> const action;
    finally(const finally&) = delete;

public:
    finally(std::function<void (void)> a)
        : action(a)
    {}

    ~finally() { action(); }
};

But in general, one should try not to carry bad Java habits over into C++.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文