deque fails to meet 23.2.1.3.4-5 causing invalid iterators and extra assignments
Steve LoBasso
slobasso@yahoo.com
Tue Jan 23 22:36:00 GMT 2007
Hi Paolo,
> first, thanks for your message. Can you help me further understanding
> the issue? I suggest discussing the current (for 4.2.0 and later
> releases) code, more efficient and also more clear, IMHO:
Speaking relative to 4.2.0 is fine, as far as I can tell, this bug exists
in all versions of gnu deque as far back as I can see. In fact I see the
same problem in the SGI implementation. It seems to be an old bug and must
have been copied MANY times into MANY implementations.
>
> ...
> const difference_type __n = __last - __first;
> const difference_type __elems_before = __first - begin();
> if (static_cast<size_type>(__elems_before) < (size() - __n) / 2)
> {
> if (__first != begin())
> std::copy_backward(begin(), __first, __last);
> _M_erase_at_begin(begin() + __n);
> }
> else
> {
> if (__last != end())
> std::copy(__last, end(), __first);
> _M_erase_at_end(end() - __n);
> }
> ...
>
> In your specific example above, we have __elems_before == 0, and the
> condition can become false only when __n == size() -1 (not for any
> smaller __n), because then integer division by two leads to zero.
> Indeed, if the condition is false, we end up copying the last element
> from the tail to the head and then erasing the last __n - 1 elements,
> the last one included, thus invalidating incorrectly any iterator to the
> last element itself. Is this the issue we are actually discussing, or
> can you conceive of other more general situations?
These seem to be the main issues:
the one extra copy and at the same time copying in the wrong direction,
thus invalidating the iterators. I can't see any other problems.
Although it is a problem more often than when n = size() - 1
ie. size() = 8 erase(begin() + 3, begin() + 4)
> Coming then to your proposed solution, I don't like that it effectively
> removes the nice idea of always copying the optimal (minimum) number of
> elements: AFAICS, only comparing to (size() - __n) / 2 exactly achieves
> that. Then, I think we could instead do this:
>
> Index: deque.tcc
> ===================================================================
> --- deque.tcc (revision 121055)
> +++ deque.tcc (working copy)
> @@ -143,7 +143,7 @@
> {
> const difference_type __n = __last - __first;
> const difference_type __elems_before = __first - begin();
> - if (static_cast<size_type>(__elems_before) < (size() - __n) / 2)
> + if (static_cast<size_type>(__elems_before) <= (size() - __n) / 2)
> {
> if (__first != begin())
> std::copy_backward(begin(), __first, __last);
>
> What do you think? In case, you should tell me whether this helps for
> the other issue too (I didn't really look into it yet, sorry, but I'm
> pretty sure it does ;) and help me a bit with understanding the dumps of
> your testcase: when, exactly, in a nutshell, the dumps are fine?
>
> Thanks,
> Paolo.
I believe your fix is just as valid as mine. Either one seems to fix the problem.
Thanks
-Steve
____________________________________________________________________________________
Cheap talk?
Check out Yahoo! Messenger's low PC-to-Phone call rates.
http://voice.yahoo.com
More information about the Libstdc++
mailing list