9

Could someone explain why I do not get the same outputs?

main.cpp:

#include <iostream>
#include <vector>

using namespace std;

struct Cell
{
    vector<int> vtx;
};

int main()
{
    vector <Cell> cells;
    Cell tmp;
    tmp.vtx.reserve(5);
    cells.push_back (tmp);
    cout << tmp.vtx.capacity() << endl;
    cout << cells[0].vtx.capacity() << endl;
    return 0;
}

Output:

5
0
Shibli
  • 5,879
  • 13
  • 62
  • 126
  • Shouldn't you print before and after `push_back` to align with your titular question? – jww Jul 13 '14 at 06:12

1 Answers1

16

Because taking a vector A and copying it to vector B does not guarantee that vector B will have the same capacity as vector A. Typically, the new vector will allocate only enough memory to hold the elements being copied into it.

In fact, there's an old trick that makes use of this, called the reduce-capacity trick:

int main()
{
   vector<int> v { 1,2,3,4,5 };
   v.clear();                   // capacity still non-zero

   vector<int>(v).swap(v);      // capacity now zero (maybe)
}

… though, technically, whether this actually works is entirely implementation-dependent.

If you move the vector in, rather than copying it, then there are no re-allocations, the buffer is actually the same buffer, and the capacity does not change:

#include <iostream>
#include <vector>

using namespace std;

struct Cell
{
    vector<int> vtx;
};

int main()
{
    vector <Cell> cells;
    Cell tmp;
    tmp.vtx.reserve(5);
    cout << tmp.vtx.capacity() << endl;
    cells.push_back (std::move(tmp));
    cout << cells[0].vtx.capacity() << endl;
    return 0;
}

// 5
// 5

(Notice that I had to move the first cout call to before the move, otherwise I'd be couting something that is in an unknown state.)

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    I'm pretty sure `vector::capacity` has no preconditions, so checking the capacity of a moved from vector should be well defined. And in the first example, wouldn't you want to default construct the temporary `vector` instead of copy constructing it? – Praetorian Jul 09 '14 at 17:57
  • @Praetorian: It may be well defined but then you'd at least expect `0`, which is not what we're looking for. And no, you wouldn't, otherwise your `v` would end up entirely empty. Swapping the vector with itself is entirely deliberate (if ugly and silly). – Lightness Races in Orbit Jul 09 '14 at 18:00
  • @Praetorian: Oh, cos of the `clear`? Sure. I should have simply popped the last element, perhaps, for a better example. – Lightness Races in Orbit Jul 09 '14 at 18:01
  • `vector(std::make_move_iterator(begin(v)), std::make_move_iterator(end(v))).swap(v);` for movable types I think is right. – Yakk - Adam Nevraumont Jul 09 '14 at 18:07
  • @Yakk: I... don't know what to say – Lightness Races in Orbit Jul 09 '14 at 18:07
  • 1
    I was going to say "you really should not copy that vectors contents, but move it", wrote the code, noticed "hmm, that won't do much for `int` now will it, and the post uses `int`, how disappointing", and decided to splort a comment out anyhow. In case you are wondering. – Yakk - Adam Nevraumont Jul 09 '14 at 18:14
  • @Yakk: No probs ;) Thanks. _My_ comment, in case you are wondering, is about the marvels of C++, which is horrid – Lightness Races in Orbit Jul 09 '14 at 18:25
  • @LightnessRacesinOrbit Well, I tend to write container/container-based converters myself: `vector( range_move( v ) ).swap(v)` reads better. It takes a pile of boilerplate however. (`template auto range_move(R&& r){ using std::begin; using std::end; return make_range(std::make_move_iterator(begin(r)), std::make_move_iterator(end(r))); }` and `templatestruct range{ Iterator begin(); Iterator end(); ... template operator C() { return {begin(), end()}; ... };` etc etc -- I cannot *wait* for concepts to finally get here.) – Yakk - Adam Nevraumont Jul 09 '14 at 20:06