Topic: standard library exception safety?
Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/10/01 Raw View
In article <324BF554.661@elastic.avid.com> "Garth A. Dickie"
<dickie@fantasia.avid.com> writes:
|> James Kanze US/ESC 60/3/141 #40763 wrote:
|> > |> I have been unable to find much discussion of exception safety
|> > |> in the C++ standard library. I would like to know what the
|> > |> behavior of, say, vector< T > is in the presence of exceptions.
|> >
|> > So would a lot of other people. There has been some discussion of this
|> > from time to time in this forum, but for the moment, nothing official
|> > from the committee. The problem definitly wasn't treated in the last
|> > public draft.
|> I have been thinking about it some more, and it seems hard to say anything
|> meaningful in the presence of non-default allocators (what do you do if
|> the assignment operator on the const_reference type throws an exception?!?).
|> It would be relatively easy to lay down some minimal guidelines, but then
|> people will want to strengthen them to the strongest possible statements,
|> which is much more difficult.
|> Here is the level of detail that I would find useful:
My comments which follow should be considered as constructive criticism
of some of the details. On the whole, I admire the work which has gone
into this proposal...
|> By a "well-behaved class" we mean a class whose destructor never
|> throws an exception.
I think that const functions, or at least some const functions, should
be equally considered. For example, it seems reasonable to me that the
comparison functions used in map, set and some of the algorithms not
throw an exception.
And of course, formally, the rule is that they do not exit by means of
an exception. If they throw an exception, but catch it before
returning, this is not a problem.
|> There should probably be a requirement that
|> all classes defined in the standard library are well-behaved whenever
|> their template arguments and all public or protected types declared
|> in those template arguments are well-behaved. (Exactly which
|> types? It doesn't really matter if a contained class declares
|> non-well-behaved classes, but it does if an allocator class has
|> a poorly-behaved pointer type.)
This creates a problem if the rule of not exiting via an exception is
extended to const functions. In particular, I can think of
implementations in which basic_string::c_str could throw an exception,
although it is const. On the other hand, I do think that the library
should guarantee that ordered comparison will not throw an exception; if
the implementation uses a representation of string in which
basic_string::data may throw an exception, then it may not use this
function in its implementation of string comparisons.
|> By a "well-behaved allocator" we mean an allocator which throws an
|> exception only from the method allocate( size_type ), and such that
|> no operations on objects of the types pointer, const_pointer,
|> reference, const_reference, size_type, etc may throw an exception.
|> (Exactly which operations?)
What about an implementation which uses an extended integer as
difference_type (or even size_type), and throws an exception on
overflow?
|> Require that the default allocator be well-behaved.
|> Require that any container class, when instantiated with a well-behaved
|> allocator and containing objects of a well-behaved class, will eventually
|> call free on all pointers allocated with allocate. (This is difficult
|> to gaurantee without a well-behaved allocator, since assigning the
|> pointer returned by allocate might throw an exception otherwise...)
|> This means that the container, in methods which allocate temporary
|> memory, must put almost everything after the allocation into a try
|> block, catch( ... ), free the temporary memory, and throw;.
|> It won't have to worry about exceptions from destroy, since it is
|> containing objects of a well-behaved class.
|> This seems like the bare minimum that should be true in order for
|> the standard library to be useful in robust code; otherwise there
|> will be memory leaks in the presence of exceptions. This is already
|> some extra work for implementors; the Modena library of 4/96 does not
|> have this property.
In addition, you have to consider construction/destruction. Basically,
for any object for which the constructor has been called and has
returned other than by an exception, the container will call the
destructor at some unspecified time (but obviously, before freeing the
underlying memory).
Also, if this is all we want to require, it should be explicitly stated
that once a function of the container has returned via an exception, the
only legal action on that container is to destruct it. (Basically, it
is undefined if the partially constructed sub-objects are cleaned up
immediatly, or in the destructor.)
|> By a "well-behaved iterator" we mean an iterator which does not
|> throw an exception from its copy constructor, assignment operator,
|> or (of course) destructor. Is it possible to make the iostream
|> iterators well-behaved?
A "well-behaved iterator" does not throw an exception, period, for
operations which are legal on it? I don't think that there is a
problem with the iostream iterators; the iostream only uses exceptions
on a user-choice basis. (Or can it throw bad_alloc on buffer
allocation?)
|> By a "well-behaved container" we mean a container which, when instantiated
|> with a well-behaved allocator and containing objects of a well-behaved
|> class, always returns new iterators which are well-behaved, and if
|> always passed well-behaved iterators as arguments, always returns
|> well-behaved iterators.
|> Require that the container classes in the standard library all be
|> well-behaved.
|> By a "copy-safe" class we mean a class whose copy constructor and operator =
|> will never throw an exception. Observe that the built-in types and all
|> POD-classes are copy-safe.
|> By a "well-behaved operation" we mean an operation which, when operating
|> on well-behaved containers and iterators, instantiated with well-behaved
|> classes and allocators, properly destroys all temporary objects created
|> during its execution in the presence of exceptions.
|> It should also, if the container is instantiated on a copy-safe class, make
|> some appropriate gaurantee depending on the nature of the operation --
|> for instance, insert might might gaurantee that the container retains its
|> original state (if the contained object is not copy-safe, insert might gauarantee
|> that all of the original objects remain in the container, with at most one
|> appearing twice; but this gets harder to specify);
|> sort might gauarantee that all of the original objects remain in the container,
|> and that the container has its original size (if the contained object is not
|> copy-safe, sort might gaurantee nothing at all...)
|> Require that all of the usual operations push_back, pop_front, insert, etc.
|> and all of the algorithms provided in the standard library are well-behaved.
|> > |> 1) If vector<T>::push_back( const T & ) fails with a bad_alloc
|> > |> exception thrown, what is the precise state of the vector
|> > |> which was being operated on? Is it gauranteed to be in the
|> > |> same state that it was in before the failed push_back call?
|> >
|> > I would guess that this is rather safe. After all, the vector must do
|> > the allocation before it can do anything else:-).
|> I think that this could be required of well-behaved containers, but
|> the requirement that the allocator be well-behaved is neccessary.
|> > More generally, although I think push_back can be written to
|> > successfully guarantee no change if an exception occurs, I do not think
|> > that this is generally the case. Imagine an exception from operator=
|> > during a sort; I don't think it at all reasonable to require the
|> > implementation to restore the vector or whatever to its initial state
|> > before sort was called.
|> If I have defined a class T which is well-behaved, but which is not
|> copy-safe (it contains some vectors, etc), I would like a gauarantee
|> that sort on a vector< T > with the default allocator will not lose
|> any memory, and will properly destroy all temporaries of type T.
|> > Part of the reason (I suspect) that the current public draft doesn't say
|> > anything about exceptions is that it is not obvious what it should say.
|> > It takes a lot of time and work to analyse, function by function, what
|> > is reasonable, and what is not. IMHO, for starters, it is reasonable to
|> > say that exiting from a const function or a destructor by an exception
|> > while in the standard library is undefined behavior, and that otherwise,
|> > the library must leave all objects in a coherent state (at least
|> > coherent enough so they can be deleted).
|> This is the goal of defining a well-behaved container, above. You can't
|> make any such claim if the user has given you a very strange allocator,
|> but you don't want to make no claim at all. Hence the well-behaved allocator
|> idea -- if I just tweak the allocator a little, I don't lose any gaurantees.
|> > The problems come in the
|> > details: are there exceptions? (string::c_str is a const function, but
|> > I can easily imagine reasonable implementations of string in which it
|> > could throw a bad_alloc.) What is meant by "coherent" in a specific
|> > context? (Think about the "sort" case. While I think that we all agree
|> > that it is not reasonable for the routine to restore the sequence to its
|> > original order, suppose it originally contained (a,b,c,d) in some order.
|> > Would (a,b,b,d) (in some order) be considered coherent? This effect can
|> > easily result if operator= throws an exception during a swap, for
|> > example.)
|> If restoration is desired, a copy of the original can be kept, I suppose.
|> > |> 2) Are there any gauarantees at all in the presence of exceptions
|> > |> thrown by T::T( const T & ), T::T( ), and T::operator = ( const T & )?
|> > |> (I think I can see how to write implementations which will deal
|> > |> well with exceptions thrown from T::T( const T & ) and T::T( ),
|> > |> but I think dealing well with an exception from T::operator = ( const T & )
|> > |> would cause efficiency problems in the implementation of insert
|> > |> and remove.)
|> >
|> > There are none that I know of in the standard. In practice, the HP
|> > implementation of vector, for example, may result in a memory (or other
|> > resource) leak; already constructed objects are *NOT* destructed, and
|> > the memory for the vector is not freed.
|> This is what I want to avoid.
|> > I am curious about your comments concerning operator=. As far as I can
|> > tell, operator= will not normally even be used in insert and remove.
|> In vector::insert, there is a call to something like copy_backward to move
|> things up in storage and make room for the inserted object. I presume that
|> the standard implementation of copy_backward uses operator =.
Probably. I've not looked at it. The effect of inserting into the
middle of a vector hadn't occured to me. (I was thinking of things like
push_back, which, of course, use the copy constructor.)
In practice, I think your distinction of "copy safe" is the right one.
Typically, the assignment operator should act like destruction of the
old value, followed by copy construction of the new one. If we accept
that destructors don't (shouldn't) throw exceptions, then this basically
means that copy constructors and assignment will/will not throw
exceptions under the same circomstances.
|> If the class T
|> is not copy-safe, then it is very difficult to revert to previous state
|> in case of an exception -- insert would have to allocate new storage large
|> enough for the whole list, copy it over with a hole, construct the new
|> element, and then replace the vector's existing storage with this new version.
|> That doesn't seem worth it, and the client code can keep its own copy of the
|> previous state if it needs to. However, if T is copy-safe, then insert
|> can only fail at the beginning, and things are easier to gaurantee.
|> (All of this in the presence of a well-behaved allocator, of course --
|> if I can't copy around references and pointers, I'm in trouble.)
While I like your idea of copy-safe, we must not forget just how
restrictive it is. No type which uses dynamic memory and does a deep
copy is copy-safe.
|> > |> Another question related to error recovery:
|> >
|> > |> 4) If a vector< T > has successfully held 50 elements at some point
|> > |> in the execution of the program, is that vector< T > gauranteed
|> > |> to still be able to hold 50 elements without throwing a bad_alloc
|> > |> exception?
|> >
|> > There are some guarantees as to when allocation may take place. For
|> > example, after executing a vector<T>::reserve( 50 ), it is guaranteed
|> > that no allocation will take place unless the vector becomes larger than
|> > 50 elements. I don't think that there are any other guarantees, but I
|> > doubt that many implementations will distinguish why they got so big,
|> > and free up memory when they get smaller if reserve has not been called.
|> It would be nice to know something like "after a call to reserve( 50 ),
|> if at any time the size of the vector is less than 50, then no allocation
|> will take place for an insertion, push_back, or assignment unless the vector
|> then becomes larger than 50 elements.
I think that this is guaranteed.
|> This allows some error recovery that is otherwise very difficult:
|> myvector.reserve( myvector.size( )); // to make sure we can put oldvector back.
|> vector< T > oldvector = myvector; // if this throws, we throw.
|> try {
|> myvector.resize( 0 );
|> while( -- count )
|> myvector.push_back( something );
|> } catch( ... ) {
|> myvector = oldvector; // cannot fail even if myvector got big first, then failed.
Cannot fail if vectors are copy-safe. The current implementation, I
think frees up the old value and then does a deep copy in this case. It
is not copy-safe.
|> }
|> In the absense of any gaurantee, I claim that I cannot safely do this operation.
Agreed.
Worse: with only the guarantees in the current draft, you cannot even
destruct the vector, much less restore it to its previous state. (In
practice, I think that the current implementations always leave the
vector in a destructable state. They will result in memory leaks,
however, and undestructed member elements.
--
James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, tudes et r alisations en logiciel orient objet --
-- A la recherche d'une activit dans une region francophone
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/26 Raw View
In article <324950DA.2180@elastic.avid.com> "Garth A. Dickie"
<dickie@fantasia.avid.com> writes:
|> I have been unable to find much discussion of exception safety
|> in the C++ standard library. I would like to know what the
|> behavior of, say, vector< T > is in the presence of exceptions.
So would a lot of other people. There has been some discussion of this
from time to time in this forum, but for the moment, nothing official
from the committee. The problem definitly wasn't treated in the last
public draft.
|> In particular:
|> 1) If vector<T>::push_back( const T & ) fails with a bad_alloc
|> exception thrown, what is the precise state of the vector
|> which was being operated on? Is it gauranteed to be in the
|> same state that it was in before the failed push_back call?
I would guess that this is rather safe. After all, the vector must do
the allocation before it can do anything else:-).
More generally, although I think push_back can be written to
successfully guarantee no change if an exception occurs, I do not think
that this is generally the case. Imagine an exception from operator=
during a sort; I don't think it at all reasonable to require the
implementation to restore the vector or whatever to its initial state
before sort was called.
Part of the reason (I suspect) that the current public draft doesn't say
anything about exceptions is that it is not obvious what it should say.
It takes a lot of time and work to analyse, function by function, what
is reasonable, and what is not. IMHO, for starters, it is reasonable to
say that exiting from a const function or a destructor by an exception
while in the standard library is undefined behavior, and that otherwise,
the library must leave all objects in a coherent state (at least
coherent enough so they can be deleted). The problems come in the
details: are there exceptions? (string::c_str is a const function, but
I can easily imagine reasonable implementations of string in which it
could throw a bad_alloc.) What is meant by "coherent" in a specific
context? (Think about the "sort" case. While I think that we all agree
that it is not reasonable for the routine to restore the sequence to its
original order, suppose it originally contained (a,b,c,d) in some order.
Would (a,b,b,d) (in some order) be considered coherent? This effect can
easily result if operator= throws an exception during a swap, for
example.)
|> 2) Are there any gauarantees at all in the presence of exceptions
|> thrown by T::T( const T & ), T::T( ), and T::operator = ( const T & )?
|> (I think I can see how to write implementations which will deal
|> well with exceptions thrown from T::T( const T & ) and T::T( ),
|> but I think dealing well with an exception from T::operator = ( const T & )
|> would cause efficiency problems in the implementation of insert
|> and remove.)
There are none that I know of in the standard. In practice, the HP
implementation of vector, for example, may result in a memory (or other
resource) leak; already constructed objects are *NOT* destructed, and
the memory for the vector is not freed.
I am curious about your comments concerning operator=. As far as I can
tell, operator= will not normally even be used in insert and remove.
|> 3) Are there any gauarantees in the presence of a T::operator &( )
|> which can throw an exception? (This is only an issue for me
|> because of a compiler bug -- to destroy a T, code for Visual C++
|> must use ( & object )->~T( ) instead of object.~T( ), in order
|> to be able to instantiate vector< someType * >.)
|> Another question related to error recovery:
|> 4) If a vector< T > has successfully held 50 elements at some point
|> in the execution of the program, is that vector< T > gauranteed
|> to still be able to hold 50 elements without throwing a bad_alloc
|> exception?
There are some guarantees as to when allocation may take place. For
example, after executing a vector<T>::reserve( 50 ), it is guaranteed
that no allocation will take place unless the vector becomes larger than
50 elements. I don't think that there are any other guarantees, but I
doubt that many implementations will distinguish why they got so big,
and free up memory when they get smaller if reserve has not been called.
--
James Kanze Tel.: (+33) 88 14 49 00 email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, tudes et r alisations en logiciel orient objet --
-- A la recherche d'une activit dans une region francophone
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu ]
Author: "Garth A. Dickie" <dickie@fantasia.avid.com>
Date: 1996/09/27 Raw View
James Kanze US/ESC 60/3/141 #40763 wrote:
> |> I have been unable to find much discussion of exception safety
> |> in the C++ standard library. I would like to know what the
> |> behavior of, say, vector< T > is in the presence of exceptions.
>
> So would a lot of other people. There has been some discussion of this
> from time to time in this forum, but for the moment, nothing official
> from the committee. The problem definitly wasn't treated in the last
> public draft.
I have been thinking about it some more, and it seems hard to say anything
meaningful in the presence of non-default allocators (what do you do if
the assignment operator on the const_reference type throws an exception?!?).
It would be relatively easy to lay down some minimal guidelines, but then
people will want to strengthen them to the strongest possible statements,
which is much more difficult.
Here is the level of detail that I would find useful:
By a "well-behaved class" we mean a class whose destructor never
throws an exception. There should probably be a requirement that
all classes defined in the standard library are well-behaved whenever
their template arguments and all public or protected types declared
in those template arguments are well-behaved. (Exactly which
types? It doesn't really matter if a contained class declares
non-well-behaved classes, but it does if an allocator class has
a poorly-behaved pointer type.)
By a "well-behaved allocator" we mean an allocator which throws an
exception only from the method allocate( size_type ), and such that
no operations on objects of the types pointer, const_pointer,
reference, const_reference, size_type, etc may throw an exception.
(Exactly which operations?)
Require that the default allocator be well-behaved.
Require that any container class, when instantiated with a well-behaved
allocator and containing objects of a well-behaved class, will eventually
call free on all pointers allocated with allocate. (This is difficult
to gaurantee without a well-behaved allocator, since assigning the
pointer returned by allocate might throw an exception otherwise...)
This means that the container, in methods which allocate temporary
memory, must put almost everything after the allocation into a try
block, catch( ... ), free the temporary memory, and throw;.
It won't have to worry about exceptions from destroy, since it is
containing objects of a well-behaved class.
This seems like the bare minimum that should be true in order for
the standard library to be useful in robust code; otherwise there
will be memory leaks in the presence of exceptions. This is already
some extra work for implementors; the Modena library of 4/96 does not
have this property.
By a "well-behaved iterator" we mean an iterator which does not
throw an exception from its copy constructor, assignment operator,
or (of course) destructor. Is it possible to make the iostream
iterators well-behaved?
By a "well-behaved container" we mean a container which, when instantiated
with a well-behaved allocator and containing objects of a well-behaved
class, always returns new iterators which are well-behaved, and if
always passed well-behaved iterators as arguments, always returns
well-behaved iterators.
Require that the container classes in the standard library all be
well-behaved.
By a "copy-safe" class we mean a class whose copy constructor and operator =
will never throw an exception. Observe that the built-in types and all
POD-classes are copy-safe.
By a "well-behaved operation" we mean an operation which, when operating
on well-behaved containers and iterators, instantiated with well-behaved
classes and allocators, properly destroys all temporary objects created
during its execution in the presence of exceptions.
It should also, if the container is instantiated on a copy-safe class, make
some appropriate gaurantee depending on the nature of the operation --
for instance, insert might might gaurantee that the container retains its
original state (if the contained object is not copy-safe, insert might gauarantee
that all of the original objects remain in the container, with at most one
appearing twice; but this gets harder to specify);
sort might gauarantee that all of the original objects remain in the container,
and that the container has its original size (if the contained object is not
copy-safe, sort might gaurantee nothing at all...)
Require that all of the usual operations push_back, pop_front, insert, etc.
and all of the algorithms provided in the standard library are well-behaved.
> |> 1) If vector<T>::push_back( const T & ) fails with a bad_alloc
> |> exception thrown, what is the precise state of the vector
> |> which was being operated on? Is it gauranteed to be in the
> |> same state that it was in before the failed push_back call?
>
> I would guess that this is rather safe. After all, the vector must do
> the allocation before it can do anything else:-).
I think that this could be required of well-behaved containers, but
the requirement that the allocator be well-behaved is neccessary.
> More generally, although I think push_back can be written to
> successfully guarantee no change if an exception occurs, I do not think
> that this is generally the case. Imagine an exception from operator=
> during a sort; I don't think it at all reasonable to require the
> implementation to restore the vector or whatever to its initial state
> before sort was called.
If I have defined a class T which is well-behaved, but which is not
copy-safe (it contains some vectors, etc), I would like a gauarantee
that sort on a vector< T > with the default allocator will not lose
any memory, and will properly destroy all temporaries of type T.
> Part of the reason (I suspect) that the current public draft doesn't say
> anything about exceptions is that it is not obvious what it should say.
> It takes a lot of time and work to analyse, function by function, what
> is reasonable, and what is not. IMHO, for starters, it is reasonable to
> say that exiting from a const function or a destructor by an exception
> while in the standard library is undefined behavior, and that otherwise,
> the library must leave all objects in a coherent state (at least
> coherent enough so they can be deleted).
This is the goal of defining a well-behaved container, above. You can't
make any such claim if the user has given you a very strange allocator,
but you don't want to make no claim at all. Hence the well-behaved allocator
idea -- if I just tweak the allocator a little, I don't lose any gaurantees.
> The problems come in the
> details: are there exceptions? (string::c_str is a const function, but
> I can easily imagine reasonable implementations of string in which it
> could throw a bad_alloc.) What is meant by "coherent" in a specific
> context? (Think about the "sort" case. While I think that we all agree
> that it is not reasonable for the routine to restore the sequence to its
> original order, suppose it originally contained (a,b,c,d) in some order.
> Would (a,b,b,d) (in some order) be considered coherent? This effect can
> easily result if operator= throws an exception during a swap, for
> example.)
If restoration is desired, a copy of the original can be kept, I suppose.
> |> 2) Are there any gauarantees at all in the presence of exceptions
> |> thrown by T::T( const T & ), T::T( ), and T::operator = ( const T & )?
> |> (I think I can see how to write implementations which will deal
> |> well with exceptions thrown from T::T( const T & ) and T::T( ),
> |> but I think dealing well with an exception from T::operator = ( const T & )
> |> would cause efficiency problems in the implementation of insert
> |> and remove.)
>
> There are none that I know of in the standard. In practice, the HP
> implementation of vector, for example, may result in a memory (or other
> resource) leak; already constructed objects are *NOT* destructed, and
> the memory for the vector is not freed.
This is what I want to avoid.
> I am curious about your comments concerning operator=. As far as I can
> tell, operator= will not normally even be used in insert and remove.
In vector::insert, there is a call to something like copy_backward to move
things up in storage and make room for the inserted object. I presume that
the standard implementation of copy_backward uses operator =. If the class T
is not copy-safe, then it is very difficult to revert to previous state
in case of an exception -- insert would have to allocate new storage large
enough for the whole list, copy it over with a hole, construct the new
element, and then replace the vector's existing storage with this new version.
That doesn't seem worth it, and the client code can keep its own copy of the
previous state if it needs to. However, if T is copy-safe, then insert
can only fail at the beginning, and things are easier to gaurantee.
(All of this in the presence of a well-behaved allocator, of course --
if I can't copy around references and pointers, I'm in trouble.)
> |> Another question related to error recovery:
>
> |> 4) If a vector< T > has successfully held 50 elements at some point
> |> in the execution of the program, is that vector< T > gauranteed
> |> to still be able to hold 50 elements without throwing a bad_alloc
> |> exception?
>
> There are some guarantees as to when allocation may take place. For
> example, after executing a vector<T>::reserve( 50 ), it is guaranteed
> that no allocation will take place unless the vector becomes larger than
> 50 elements. I don't think that there are any other guarantees, but I
> doubt that many implementations will distinguish why they got so big,
> and free up memory when they get smaller if reserve has not been called.
It would be nice to know something like "after a call to reserve( 50 ),
if at any time the size of the vector is less than 50, then no allocation
will take place for an insertion, push_back, or assignment unless the vector
then becomes larger than 50 elements.
This allows some error recovery that is otherwise very difficult:
myvector.reserve( myvector.size( )); // to make sure we can put oldvector back.
vector< T > oldvector = myvector; // if this throws, we throw.
try {
myvector.resize( 0 );
while( -- count )
myvector.push_back( something );
} catch( ... ) {
myvector = oldvector; // cannot fail even if myvector got big first, then failed.
}
In the absense of any gaurantee, I claim that I cannot safely do this operation.
Regards,
Garth A. Dickie
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]
Author: "Garth A. Dickie" <dickie@fantasia.avid.com>
Date: 1996/09/25 Raw View
I have been unable to find much discussion of exception safety
in the C++ standard library. I would like to know what the
behavior of, say, vector< T > is in the presence of exceptions.
In particular:
1) If vector<T>::push_back( const T & ) fails with a bad_alloc
exception thrown, what is the precise state of the vector
which was being operated on? Is it gauranteed to be in the
same state that it was in before the failed push_back call?
2) Are there any gauarantees at all in the presence of exceptions
thrown by T::T( const T & ), T::T( ), and T::operator = ( const T & )?
(I think I can see how to write implementations which will deal
well with exceptions thrown from T::T( const T & ) and T::T( ),
but I think dealing well with an exception from T::operator = ( const T & )
would cause efficiency problems in the implementation of insert
and remove.)
3) Are there any gauarantees in the presence of a T::operator &( )
which can throw an exception? (This is only an issue for me
because of a compiler bug -- to destroy a T, code for Visual C++
must use ( & object )->~T( ) instead of object.~T( ), in order
to be able to instantiate vector< someType * >.)
Another question related to error recovery:
4) If a vector< T > has successfully held 50 elements at some point
in the execution of the program, is that vector< T > gauranteed
to still be able to hold 50 elements without throwing a bad_alloc
exception?
Thanks for any information you can provide.
Regards,
Garth A. Dickie
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std-c++@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++-request@ncar.ucar.edu
]