Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to use `std::vector.back()` correctly?

I have a bug in my code which I don’t quite understand. According to the documentation, std::vector.back() returns a reference to the last element in the container, so here’s what I did: (live here)

#include <iostream>
#include <vector>

class Foo {
  public:
    Foo(int id) : id(id) {
        std::cout << "foo " << id << " constructed" << std::endl;
    }
    ~Foo() {
        std::cout << "foo " << id << " destructed" << std::endl;
    }

    int id;
};

int main() {
    std::vector<Foo> foos;
    
    for (int i = 0; i < 2; i++) {
        foos.emplace_back(i);  // construct foo in place
        
        auto& foo = foos.back();
        std::cout << "Play with " << foo.id << std::endl;
    }
    
    for (auto&& foo : foos) {
        std::cout << "I'm foo " << foo.id << std::endl;
    }
    
    return 0;
}

which yields the following output:

foo 0 constructed
Play with 0
foo 1 constructed
foo 0 destructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed

So, right after the second instance of Foo is constructed, the first one gets destructed, why? because auto& foo goes out of scope? But when I print the foos vector, it still has two foos, what a surprise! At first glance it seems like foos.back() is making a copy, which is not true. What I really don’t understand is, how is it possible that the constructor is called twice but the destructor is called 3 times, shouldn’t they always come in pair?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

What’s happening here is that when you .emplace_back(1) the std::vector does not have enough space for two elements, so it has to reallocate, copy/move all the existing elements to the new allocation, then emplace_back the new one. Hence you see the only existing element being destroyed from the old allocation.

This goes away if you .reserve(2) before the loop: https://godbolt.org/z/oj8fWhznP

foo 0 constructed
Play with 0
foo 1 constructed
Play with 1
I'm foo 0
I'm foo 1
foo 0 destructed
foo 1 destructed

You can also use .capacity() to check the size of the allocation, which indeed confirms the theory: https://godbolt.org/z/a88xfqa7f

--- 0 ---
capacity: 0
foo 0 constructed
capacity: 1
Play with 0
--- 1 ---
capacity: 1
foo 1 constructed
foo 0 destructed
capacity: 2
Play with 1

Adding logging definitions for move/copy constructors and assignment operators might give you better insight into what std::vector is doing internally.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading