Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange reference behaviour in C++

Tags:

c++

I have fairly short question:

vector<int> ints{1,2,3,4};

int &y = ints[0];

// ints.push_back(y);

y = 5;

for (auto &x : ints) {
    cout << x << ", ";
}

Why while commented you get 5, 2, 3, 4 but when you uncomment ints.push_back(y); you get 1, 2, 3, 4, 1. I'll write it once more in order to be perfectly clear about the problem: you are getting [-->1<--], 2, 3, 4, 1, instead of 5, 2, 3, 4, 1 or even 5, 2, 3, 4, 5

This behaviour drives me nuts... What happens under the hood?

like image 570
Lu4 Avatar asked Nov 30 '22 00:11

Lu4


2 Answers

When you push back onto a vector, if the vector doesn't have enough memory allocated to store the new element, it allocates a new larger memory block, moves the elements over, and frees the old memory. If this happens, any references or pointers to elements of the vector are invalidated.

So, after the push back, when you then assign 5 to y, it is undefined behavior, because y is an invalid reference, and there is no reason to expect the assignment to have any effect on any elements of the vector.

like image 193
Benjamin Lindley Avatar answered Dec 15 '22 22:12

Benjamin Lindley


You need to consider the reallocation that might happen when std::vector::push_back is called.

int &y = ints[0];

ints.push_back(y);

y = 5;

The third instructions may cause undefined behavior if such reallocation is performed. If that happens, you lose all the guarantees you could think of. Taking out this condition, by reserving memory, the expected result is shown

5, 2, 3, 4, 1,

std::vector<int> ints{1,2,3,4};
ints.reserve(128); // Note

int &y = ints[0];

ints.push_back(y);

y = 5;

The table at cppreference shows what happens precisely and when iterators are invalidated.

Usually, you don't want to have free references or pointers to elements stored in a container that could be invalidated; if that's truly needed, reload the address or choose a container that offers more guarantees about reallocation such as std::deque, std::forward_list or std::list.

like image 40
edmz Avatar answered Dec 15 '22 23:12

edmz