Re: exception behaviour in containers
Daniel Kr=FCgler wrote:
On 3 Okt., 23:29, DFC <d...@clovermail.net> wrote:
Daniel Kr=FCgler wrote:
In [container.requirements]/10+11
of the recent draft there are given general statements on insert for
the
single-insertion step (so this is not limited to unordered associative
containers).
Thanks, I missed that one this time around but it does look familiar, I=
see it
is in 23.1/10 of my copy of 14882-2003 as well.
Yes, that is correct, but at least for the following discussion the
draft
contains at least some more precise wordings, v.i. In the following I
will - if not said otherwise - refer to the recent draft.
I think it was considered as too expensive to provide the
strong guarantee for multi-inserts and if needed is easy to write.
This is what I was wondering but wanted to check really. operator= i=
s the worst
one because if you want to gaurantee complete success/fail of operation=
you have
to allocate copies of all the source nodes before deleting the current =
ones. The
insert could know what it has inserted and then back track, which could=
be quite
simple.
Especially the copy-assignment operator of containers has a very
sparse
specification (actually only a post-condition and a complexity is
given),
[container.requirements]/3 says something about the requirements on
the
value_type, if it is used. In the moment of writing this I have the
impression,
that the container library is under-specified in this regard, because
in
[container.requirements]/11 it is said:
"Unless otherwise specified (either explicitly or by defining a
function in terms
of other functions), invoking a container member function or passing a
container
as an argument to a library function shall not invalidate iterators
to, or change
the values of, objects within that container."
Since operator= is *not* defined in terms of other functions
(specifically: Not
via insert) one could interpret this sentence that op= of a container
must not
invalidate iterators to the *destination* container (we are obviously
using a
member function of the destination) - which makes no sense at all. Can
anyone
show me some wording in the standard, which clarifies this point?
Hmm, it would seem operator= should be noted that it invalidates all it=
erators
and references?
Concerning your reference to "nodes": Are you speaking here for
std::list
now, otherwise the remainder of the above mentioned insert conclusion
cannot be guaranteed.
Not quite sure I follow you here but yes I was thinking about list when I=
wrote
that but really I am concerned with any container and its nodes or elemen=
ts.
When you say it is 'easy to write' you mean easy for the user to add ex=
tra code
to guarantee this if needed? i.e they would make a copy of the contain =
before
performing the operator= or insert(many) and if it failed they would =
revert to
the copy.
Yes, I mean a user-written helper function, typically taking advantage
of
swap:
template<class Container>
Container& safe_copy_assign(Container& dst, const Container& rhs) {
dst.swap(Container(rhs));
return dst;
}
Insert is a more tricky case, because of the first hint-iterator
argument,
but it is also solvable.
So in summary, what you are saying is an implementation can't be expect=
ed to
provide this guarantee for any operation that involves some sort of mul=
ti-insert
but the question is, is an implementation is still allowed to provide t=
hat if it
wants?
That depends. Just in the moment I see no way how e.g. std::vector
could realize that. The point is that this seems to violate the
reallocation/
reference/iterator guarantees of this container: If I make a copy of
the original
container to undo the changes of a possibly failing insert, I would
not be allowed to replace the original container. But except for some
specific cases an implementation can give such guarantees if not
violating other constraints.
The vector allocates a new larger chunk of memory capable of holding the
enlarged vector, copies the old elements and new to be inserted ones, and=
then
deletes the original. If anything goes wrong it deletes the new one and =
the old
one still exists. If the vector is already large enough it copies everyt=
hing it
needs to the right and inserts new elements. If something goes wrong it =
copies
them all back. (If there is an exception copying elements back it is bad =
news,
perhaps this is the relevance of the "other than the assignment operator =
of T"
clause, the way I am reading it... see below )
Further-on several containers to give more precise guarantees, e.g.
in [deque.modifiers] or [vector.modifiers]
and so does list.modifiers (again I had overlooked these). So it seems =
all the
sequences do require insert(many) to back track if there is an exceptio=
n.
How do you come to that conclusion? Please note, that they mostly
give some constraints, e.g. std::vector says in [vector.modifiers]/3:
"[..] If an exception is thrown other than by the copy constructor or
assignment operator of T or by any InputIterator operation there are
no effects.[..]
The way I read this (but perhaps I misunderstand it) is that, for example=
,
23.2.5.4/3 is referring to all the various insert methods outlined in /1.=
/3 to
me reads "if an exception is thrown...there are no effects" in other word=
s "if
an exception is thrown when calling an insert method...the container must=
be in
the same state as before the insert method was called" (+ some cases when=
this
may not happen depending on the exception source). If this is right it a=
nswers
some of my original question... for insert(many) in sequence containers i=
t is
explicitly stated what needs to happen.
First, note that compared to [lib.vector.modifiers]/1 of the
corresponding
14882-2003 document the part "or by any InputIterator operation" has
been added. IMO this has been done, because other iterator categories
can precalculate the new size and ensure that there is enough memory
to fulfil the request - this is generally not possible for
InputIterators.
Second, if you have the money (I mean the allocated memory ;-)),
other
potential exception sources are those which occur by moving some
vector
elements and by copying the new ones - and if these fail, there can
be
effects.
Ok, agreed. If the exception source is, for example, due to copying the n=
ew one
we may still be able to undo, but if it is due to copying the old ones ba=
ck in
an attempt to undo then we are stuffed, so I now understand the reason fo=
r the
"other than copy ctor or assignment op of T..." clause.
Do
you think this requirement on the associative containers is an oversigh=
t,
because like you mentioned it was rushed? I also can't see the signifi=
cance of
"other than by the copy constructor or assignment operator of T" except=
ion to
the rule there are no effects, any thoughts?
Hmmh, now you are discussing again about the *associative* containers?
What do you mean with "this requirements" in this regard?
What I am trying to say is I can't see any mention of what should happen =
if
there is an exception in a map::insert for example, 23.3.1.3 doesn't seem=
to
mention exceptions at all. I want to know if there should be a paragraph =
like
23.2.5.4/3 in the appropriate places in the associative containers, or if=
there
is a good reason nothing is mentioned about exceptions, or if I have over=
looked
something (again).
So in summary:
* I think we have answered my questions about sequence containers and ins=
ert methods
* operator= probably isn't very well specified
* I'm not sure about insert methods in the associative containers and nee=
d some
more advice about what should happen with these
Daniel
---
[ 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 ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]