Re: best way to "delete" all objects in a std::vector.
James Kanze wrote:
On Jun 5, 9:22 am, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
Jerry Coffin wrote:
In article <daniel_t-4F9851.22361904062...@earthlink.vsrv-
sjc.supernews.net>, danie...@earthlink.net says...
[ ... ]
AFAIK, the implementation I presented is guaranteed by the
standard not to read or copy any pointers after the delete.
I don't see any such guarantee, though I'll admit I might
have missed it.
Maybe you missed it because you snipped it. So, let's look at it:
struct Delete_ptr {
template<class T> T* operator()(T* p) const { delete p; return 0; }
};
...
transform(s.begin(),s.end(),s.begin(),Delete_ptr());
Note:
a) Delete_ptr::operator() returns 0.
b) The proposed solution uses transform and not foreach.
And... Where do you have a guarantee that std::transform
doesn't read through the pointer a second time, after having
called the fucntional object? Or that
std::vector::iterator::operator*() doesn't read the object
before returning an lvalue to it.
Those are valid points.
I very heavily stressed in my original posting that this was for
nitpickers, because, of course, no implementation will do such
things, and we all know it. But the fact remains that the
standard requires that all objects in a container be copyiable,
at all times (even if no member function is called). And
between the delete in the operator(), above, and the moment
transform assigns the result of the operator to its target,
there is a deleted pointer in the container, which is undefined
behavior.
If that was true in this generality, I would consider it a defect in the
standard because it decidedly interferes with generic programming. E.g.,
delete_and_set_null ( T* & p_ref ) {
delete p_ref;
p_ref = 0;
}
would be undefined when v[0] is passed into it because between the two
statements, although no member function of the container can be called,
there is a non-copiable object in the container. Instead one would have to
use your swap proposal to implement a safe delete_and_set_null (at the
point the reference is passed, no template magic will be able to detect
that is comes from a container). Clearly, that makes you pay for stuff you
don't use.
BTW, with regard to the "at all times (even if no member function is
called)", the standard is not at all clear. [23.1/3] requires the "type of
objects stored in these components" to satisfy the CopyConstructible and
Assignable requirements, which in turn are only defined for types and not
for objects. This is also emphasized in [20.1/1] which implies that
CopyConstructible is a requirement for types used to instantiate template
arguments not a requirement for objects. The type "T*" clearly satisfies
those requirements. That individual objects of type T* may at times have
invalid values does not change that. However, other rules kick in and say
that reading such values is undefined. In that regard, your objections from
the first paragraph are valid. However, that can only happen when member
functions of the container are called triggering the read. Mere existence
of an object with invalid value within a container might not necessarily
entail undefined behavior (at least of the top of my head, I don't see the
provisions that would imply that).
Best
Kai-Uwe Bux