"David Abrahams" <[EMAIL PROTECTED]> wrote in message [EMAIL PROTECTED]">news:[EMAIL PROTECTED]... > [...] > It sure does if any of the bases or members of smart_ptr throws > from its constructor.
Just when I thought the problem was solved... > [...] > I dunno. Acquiring ownership at construction time is a key part > of the "one true meaning" of RAII (not the accepted meaning, > which has come to be "deallocating resources in destructors" -- a > concept having nothing to do with acquisition _or_ initialization). > There's a good reason for this, since it avoids the problem of > leaking when a constructor throws. On the other hand, acquiring multiple resources is going to present exactly the same problem. Or acquiring a resource in any other context when members can throw will have the same problem. You could say that no class should acquire a resource and have other members/bases that could throw, but that seems draconian. So what is the real solution? I'm beginning to agree with Andrei that there appears to be a fundamental language problem here, but I don't pretend to have a change that would fix it. Modifying function try blocks to be more useful might seem like the answer, but any change would make them different from normal try blocks, right? Look at shared_count. It does not acquire the count in the initializer list. It default-constructs and then acquires the count inside a try block in the body of the c'tor. Does that mean it does not perform "True RAII"? I would like to use the initializer list as much as the next guy, but multiple initializers seem to be a real problem. It works great for the trivial case. Does this mean "True RAII" doesn't scale? And this is an RAII problem, not a PBD or orthogonality problem. That's because as much as we would like to say that every class should only perform one function, it happens that some functions cannot be performed with a single resource. For ref-counted smart pointers (shared_ptr or smart_ptr), those resources are the managed resource and the count. A kludge, but perhaps a start in the right direction, would be to have a special function analogous to the destructor that gets called when a member is destroyed as the result of a failed construction. It would be analogous to placement delete. It would only get called on fully constructed objects. This way, objects would be able to tell when they are being destroyed as part of their normal life cycle, and when they are being destroyed due to a problem. One syntax would be to pass a dummy int parameter to a normal destructor, like so: scalar_storage::scalar_storage() { // normal destruction, do nothing } scalar_storage::~scalar_storage(int) { // something bad happened, clean up with extreme prejudice destroy(); } Another syntax might be to pass the exception type to the destructor. Then, a given destructor would only get called if its argument were convertible to the thrown exception type, like so: scalar_storage::~scalar_storage(std::bad_alloc const&) { // failed allocation somewhere else, clean up destroy(); } scalar_storage::~scalar_storage(std::exception const&) { // some other problem, clean up anyway ;) destroy(); } In the event that no specialized d'tor were defined, the normal destructor would get called. So, in this case: int main() { ref_counted r; } At the end of scope, ref_counted::~ref_counted() is called. Whereas, in this case: struct X { struct Y { Y() { throw 0; } }; X() : r(), y() { } ref_counted r; Y y; }; ref_counted::~ref_counted(int) would get called. Unfortunately, shared_count shows that this scheme is not sufficient, because you lose the context of the throwing c'tor. In particular, the shared_count(P, D) c'tor is most problematic. On the other hand, shared_count could technically be written using a function try block. Who knows. Maybe someone can come up with a better idea. Dave _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost