Evandro Coan
Evandro Coan

Reputation: 9466

Why I cannot take a alias to a std::vector element?

On the slide 6 at Rust for C++ programmers, there is this code:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> v;
    v.push_back("Hello");

    string& x = v[0];
    v.push_back("world");

    cout << x << endl;
    return 0;
}

Running it I got:

g++ --std=c++11 main.cpp -I . -o main
./main
P▒▒o▒Y ▒▒2.▒8/.▒H/.▒H/.▒X/.▒X/.▒h/.▒h/.▒x/.▒x/.▒▒/.
@▒▒
...

And it keeps going for much more stuff. I found some question about aliases and vectors as:

  1. int vs const int&
  2. Changing things from a vector

But I could not figure out why the alias is not working based on them. I looked over the http://en.cppreference.com/w/cpp/container/vector, about the vector definition, however it does just seem to be continue memory allocated on the disk. I understand the string Hello and world are allocated somewhere on the data member of the program, as on the assembly here by g++ main.cpp -S:

...
.lcomm _ZStL8__ioinit,1,1
    .def    __main; .scl    2;  .type   32; .endef
    .section .rdata,"dr"
.LC0:
    .ascii "Hello\0"
.LC1:
    .ascii "world\0"
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
...

If I do not push the second element world, the program correctly runs. Therefore why the alias is loosing the reference to the first vector element after the second push?

Upvotes: 3

Views: 1673

Answers (3)

Vlad from Moscow
Vlad from Moscow

Reputation: 311058

When the method push_back was called the vector can reallocate the used memory and as result the reference becomes invalid.

You could reserve enough memory before adding new elements to the vector. In this case the reference will be valid. For example

vector<string> v;
v.reserve( 2 );

v.push_back("Hello");

string& x = v[0];
v.push_back("world");

Here is a demonstrative program

#include <iostream>
#include <vector>
#include <string>

int main() 
{
    std::vector<std::string> v;
    v.reserve( 2 );

    v.push_back("Hello");

    std::string& x = v[0];
    v.push_back("world");

    std::cout << x << ' ' << v[1];
    std::cout << std::endl;

    return 0;
}

Its output is

Hello world 

Upvotes: 4

Peter
Peter

Reputation: 36617

push_back() resizes the vector (that's intrinsic in adding an element to it).

That invalidates all iterators, pointers, and references that refer to elements of that vector.

Accessing elements of a vector through an invalidated iterator, pointer, or reference (i.e. that were valid before the resizing operation, but not after) gives undefined behaviour.

x is invalidated by the call of push_back() that occurs after its initialisation, and before the output statement

Upvotes: 3

Lucinda Rigetti
Lucinda Rigetti

Reputation: 816

When you do the second push_back iterators and references should be assumed to be invalidated. The vector may probably resize its data block - most likely at another memory location.

As such the variable reference x is referencing unallocated memory which subsequently leads to undefined behavior.

Upvotes: 7

Related Questions