-2

I'm trying to erase an element from a vector by index as follows, but why does the first output differ from the second output? Is there any way of avoiding this or a better way of removing elements?

int main() {
    vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cout << *test.end() << endl;
    test.erase(test.begin() + 2);
    cout << *test.end() << endl;
    return 0;
}
AndiChin
  • 161
  • 2
  • 6
  • 1
    end() is an element after the last one, dereference it has no sense – vsh Dec 25 '19 at 02:09
  • 1
    `*test.end()` is Undefined Behaviour. – Richard Critten Dec 25 '19 at 02:10
  • I'm just trying to prove that end did in fact change, because sometimes I may want to compare them – AndiChin Dec 25 '19 at 02:11
  • 3
    You can't "prove" anything by dereferencing an undereferenceable iterator. – Lightness Races in Orbit Dec 25 '19 at 02:13
  • 1
    _"sometimes I may want to compare them"_ That doesn't make sense either; if you were to cache the result of `test.end()` before the `erase` and use it after the `erase`, you'd again have undefined behaviour (even with a `==`) because the iterator would have been invalidated. What is your _real_ question? – Lightness Races in Orbit Dec 25 '19 at 02:14
  • but end becomes defined after I erase an element; it becomes 10, how do I stop this from happening? – AndiChin Dec 25 '19 at 02:15
  • No, it does not. The "value" you see is unspecified and meaningless. Why do you think you need to "stop it from happening"? What are you _really_ trying to do? – Lightness Races in Orbit Dec 25 '19 at 02:16
  • I got 61809 for the first output, and 10 for the second output – AndiChin Dec 25 '19 at 02:17
  • @WhalalalalalalaCHen -- FYI, your code instantly produces an `assert()` dialog and stops dead when `test.end()` is dereferenced using Visual Studio. Thus no output is produced, only a terminated program. This is what others have tried to state to you -- dereferencing `end()` is bogus. – PaulMcKenzie Dec 25 '19 at 02:57
  • You cannot execute erase without changing `end()`, period (except special case when erase is a noop) – Slava Dec 25 '19 at 02:59
  • Does this answer your question? [Behavior when dereferencing the .end() of a vector of strings](https://stackoverflow.com/questions/26965621/behavior-when-dereferencing-the-end-of-a-vector-of-strings) – Andras Deak -- Слава Україні Feb 19 '20 at 22:59

4 Answers4

4

std::vector::end returns the iterator to the element following the last element, dereference on it leads to UB, means anything is possible, it's just meaningless.

Returns an iterator to the element following the last element of the container.

This element acts as a placeholder; attempting to access it results in undefined behavior.

You might use std::vector::back to get the last element; (and better to check whether the vector is empty or not in advance.)

int main() {
    vector<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    cout << test.back() << endl;
    test.erase(test.begin() + 2);
    cout << test.back() << endl;
    return 0;
}
Community
  • 1
  • 1
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • but after I erase the element, end() becomes 10, which is defined, how do I avoid this? – AndiChin Dec 25 '19 at 02:14
  • @WhalalalalalalaCHen end() does _not_ "become 10". Stop dereferencing it. You cannot do that. – Lightness Races in Orbit Dec 25 '19 at 02:15
  • @WhalalalalalalaCHen Just don't dereference on `end` which has UB. What do you want to avoid? – songyuanyao Dec 25 '19 at 02:16
  • I want to check if an element exists by doing find(test.begin(), test.end(), element) == test.end() – AndiChin Dec 25 '19 at 02:20
  • Then do that. What does that have to do with what you put in your question, or what you keep saying in your comments? – Lightness Races in Orbit Dec 25 '19 at 02:20
  • @WhalalalalalalaCHen Did you get any trouble with `find(test.begin(), test.end(), element) == test.end()`? After perform `erase`. – songyuanyao Dec 25 '19 at 02:22
  • @WhalalalalalalaCHen What's the output? `find(test.begin(), test.end(), element) == test.end()` would return `false`, meaning `find(test.begin(), test.end(), element)` is not `test.end()` and `10` is found. [LIVE](https://wandbox.org/permlink/Q2XOfrMXDJNX1BKq) – songyuanyao Dec 25 '19 at 02:25
  • ohh thank you so much, I get it now, dereferencing end() confused me as it gave me weird outputs – AndiChin Dec 25 '19 at 02:28
  • @WhalalalalalalaCHen That's what UB means, anything is possible. Just avoid it. – songyuanyao Dec 25 '19 at 02:28
  • Following your statement *and better to check whether the `vector` is empty or not in advance*, I think it would be recommendable to check that the `vector` has at least three elements because of the `test.erase(test.begin() + 2);`. You can perform this check and forget about the first one. – JFMR Dec 25 '19 at 09:49
1

normally you can not print the *test.end() , because it is not the last pointer, it is after the last pointer. it's value is not valid for you,and may cause an exception.

vector::end() == vector::begin() + vector::size()

you can use vector::back

I checked the stl code of vector::erase()

    iterator erase(const_iterator _Where) noexcept(is_nothrow_move_assignable_v<value_type>) /* strengthened */ {
        const pointer _Whereptr = _Where._Ptr;
        auto& _My_data          = _Mypair._Myval2;
        pointer& _Mylast        = _My_data._Mylast;

#if _ITERATOR_DEBUG_LEVEL == 2
        _STL_VERIFY(
            _Where._Getcont() == _STD addressof(_My_data) && _Whereptr >= _My_data._Myfirst && _Mylast > _Whereptr,
            "vector erase iterator outside range");
        _Orphan_range(_Whereptr, _Mylast);
#endif // _ITERATOR_DEBUG_LEVEL == 2

        _Move_unchecked(_Whereptr + 1, _Mylast, _Whereptr);
        _Alty_traits::destroy(_Getal(), _Unfancy(_Mylast - 1));  // here after move it destroy the last element.
        --_Mylast;
        return iterator(_Whereptr, _STD addressof(_My_data));
    }

so use *end() may cause a exception of wild pointer.

halong
  • 70
  • 6
  • 2
    You cannot do `*test.end()` neither normally nor abnormally and you do not need to check code for that, documentation is enough. It is not a pointer but iterator. – Slava Dec 25 '19 at 02:56
0

try list

 #include <vector>
 #include <list>
   int main() {
    list<int> test = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    auto pbeg=test.begin(),
    pend=test.end(),
    a = pbeg, b = pend;
    cout << *(--b) << endl;
    ++(++a);
    test.erase(a);
    cout << *(--b) << endl;
    for ( a=pbeg; a!=pend; ++a)
        cout<< *a <<" ";

    return 0;
}
10
10
1 2 4 5 6 7 8 9 10
0

Your choice of the container needs to be revisited if you want avoid iterator invalidation during erase or removal of items from container.

Below link can provide the overview of iterator invalidation:

http://kera.name/articles/2011/06/iterator-invalidation-rules-c0x/

Build Succeeded
  • 1,153
  • 1
  • 10
  • 24