-1

I wrote a program to insert the numbers in C++ with CLion.

First, enter a set of positive integers and place them into the vector called pour. Then, take the negative numbers of these positive integers and then emplace them into the pour. However, when I tried to use the range-based for loop to insert the negative numbers into the pour, the last element always gave a random number without any reason. But once I don't use the range-based loop, the last element won't give me a random number.

#include <iostream>
#include <vector>

using namespace std;

int n , m ;
vector<int> pour ;

int main()
{
    for (int i=0 ; i<3 ; ++i)
    {
        cin >> m ;
        pour.emplace_back(m) ;
    }
    for (auto i : pour)
    {
        pour.emplace_back(-i) ;
    }
    for (auto i : pour)
    {
        cout << i << endl ;
    }
    return 0;
}
  • 5
    modify a container while iterating it is generally frowned upon. – NathanOliver Mar 21 '23 at 17:52
  • 1
    emplacing into a vector invalidates iterators. If you compare to old-style iterator based loop it might be more obviosu what goes wrong – 463035818_is_not_an_ai Mar 21 '23 at 17:54
  • or consider `for (auto i : pour) pour.emplace_back(-i);` would work, why do you expect it to stop? You'll never reach the end of the vector – 463035818_is_not_an_ai Mar 21 '23 at 17:56
  • @463035818_is_not_a_number Oh, I get it, thanks. But there's another question, why the range-based loop won't become a infinite loop? – 抹茶抹茶 Mar 21 '23 at 18:00
  • 2
    It could. The behaviour is undefined, so the program could do anything including what you expected to happen or hacking the Pentagon and ordering a nuclear strike on your backyard. – user4581301 Mar 21 '23 at 18:07
  • 1
    *why the range-based loop won't become a infinite loop?* -- "Why when I tie a 600 pound weight to a rope that is rated to hold only 500 pounds, the rope doesn't break?" When you break the rules, anything can happen. That includes "work", "crash", work sometimes, crash other times, etc. – PaulMcKenzie Mar 21 '23 at 18:08
  • _"...If the new size() is greater than capacity() then all iterators and references (including the end() iterator) are invalidated. Otherwise only the end() iterator is invalidated...."_ and as range based for loops use iterators we have UB. – Richard Critten Mar 21 '23 at 18:18
  • To get this to work you need to adjust the capacity of the container so that you can emplace new elements without invalidating existing iterators. To do that, use [`vector::reserve`](https://en.cppreference.com/w/cpp/container/vector/reserve). – Pete Becker Mar 21 '23 at 18:24
  • Reopened. Yes, there are other questions with answers that discuss iterator invalidation. But they don't address the actual question here, which is how to add elements **without** invalidating iterators. – Pete Becker Mar 21 '23 at 19:31
  • @463035818_is_not_a_number -- emplacing into a vector invalidates iterators **if the vector has to be resized**. The key here is to do the emplace without resizing. – Pete Becker Mar 21 '23 at 19:33
  • @PeteBecker perhaps this is a better dupe https://stackoverflow.com/questions/68095269/taking-input-into-vector-using-for-range-loop?rq=2 – 463035818_is_not_an_ai Mar 21 '23 at 19:36
  • @463035818_is_not_a_number -- that's better, but still not quite right. The issue here is reading the initial contents of the vector while appending new stuff. There's a bit more to it than just reading into a pre-sized vector. – Pete Becker Mar 21 '23 at 20:06

1 Answers1

0

As one of the comments suggests, this question talks about the difference between the number of elements in a vector and the capacity of the vector. The capacity is the number of elements that the vector could hold, not the number it actually holds. You can append elements to the vector (with push_back or emplace_back) until the vector hits its capacity and the vector won't need to reallocate its storage, so iterators and pointers to its elements remain valid. That's the key here: adjust the capacity so that you can work through the initial elements of the vector and append the new values without triggering a reallocation.

So, once you've got the initial elements into pour, adjust its capacity so that it's large enough for the insertions, and then do them:

pour.reserve(2 * pour.size()); // réserve space for twice as many elements
auto first = pour.begin();
auto last = pour.end();
for ( ; first != last; ++first)
    pour.emplace_back(-*first);

This puts the new elements at the end of the vector. If the task is to put the negation of each element immediately after the element (e.g., 1, -1, 2, -2), the bookkeeping is a bit more tricky, but the principle is the same: adjust the size first, then iterate through the initial elements and put in the new values.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165