Имитация блока наконец в C ++ 0x - PullRequest
4 голосов
/ 29 мая 2011

Вдохновленный из другой темы , я написал этот код, который имитирует блок 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

@ Йоханнес, похоже, считает, что это не совсем правильно, и заметил, что :

Может произойти сбой, если компилятор не исключит временное вКопировать инициализацию, потому что затем он удаляет дважды с одним и тем же значением указателя

Я хотел бы знать, как именно.Помогите мне разобраться в проблеме: -)


РЕДАКТИРОВАТЬ:

Проблема устранена как:

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

Ответы [ 2 ]

8 голосов
/ 29 мая 2011

В этой инициализации:

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

Может использоваться неявно определенный конструктор копирования для lambda. Это просто скопирует необработанный указатель pbase, который затем будет удален более одного раза.

1008 * Е.Г. *

$ 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)

На самом деле, ваш запуск assert маскирует проблему двойного удаления, но это демонстрирует сбой, который я выделил.

$ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors -DNDEBUG lambdafun.cc 
$ ./a.out 
Segmentation fault (core dumped)
2 голосов
/ 02 июня 2011

Кажется, намного сложнее, чем необходимо.Почему бы просто:

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 ++.

...