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 开发者_JAVA百科 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
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)
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++.
精彩评论