10

While reading code I see that:

vector<TypeA>(typeAObj).swap(typeAObj);

My question is

Why do they swap a vector with a copy of itself?

Dmitriy Kachko
  • 2,804
  • 1
  • 19
  • 21
  • 1
    FWIW, [it isn't _guaranteed_ to do anything](http://stackoverflow.com/questions/7829018/can-we-rely-on-the-reduce-capacity-trick). – Lightness Races in Orbit Feb 21 '12 at 15:12
  • Note that the vector is not swapped with *itself*, but rather with a *copy* of itself. – David Rodríguez - dribeas Feb 21 '12 at 15:14
  • I wrote about copy, Rawicki edited it out )) – Dmitriy Kachko Feb 21 '12 at 15:18
  • @Lightness: although it's legal, it would be fairly perverse for an implementation to copy the capacity in the copy ctor. And that's the only way I can think of that this swap wouldn't at least sometimes reduce capacity. What I would be unsurprised to find, though, is that an implementation does some "rounding up" with capacities, so you might find it impossible to reduce capacity from, say, 16 to 15. – Steve Jessop Feb 21 '12 at 15:20
  • @Steve: I prefer to code to guarantees. And, yes, then I'm still relying on toolchain compliance but, yes, that's still optimal :P – Lightness Races in Orbit Feb 21 '12 at 16:31
  • @Lightness: sure, I'm not saying you should deliberately write programs that will fail if it doesn't reduce capacity. After all, there's no guarantee that an implementation won't arbitrarily allocate a few hundred MB of RAM for pretty much no predictable reason at all, we know that in general we cannot control resource use precisely and we can't write portable code that relies on doing so. I'm just saying that it's a reasonable QoI expectation, that it will probably free some memory if size is much smaller than capacity. – Steve Jessop Feb 21 '12 at 16:42

2 Answers2

12

That's a pattern for shrink-to-fit in C++03, where there is no such operation in the interface of the vector class. What the code does is creating a copy (hopefully the capacity of the vector will be close to the number of available elements) and then swaps it with the original vector. After the expression completes, the temporary (which now holds the original buffers) is discarded and the memory is released.

Consider:

std::vector<int> large;
large.reserve( 10000000 );  // might be the result of multiple push_back/erase
// large.capacity() >= 10000000
large.push_back( 1 );       // Make more explicit that 'large' might not be empty
std::vector<int>( large ).swap( large ); 
// large.capacity() is hopefully closer to 1

In C++11 the vector type has been modified to provide a shrink_to_fit operation that takes on that role. It is important to note that neither the old pattern nor shrink_to_fit are binding operations, that is, there is no guarantee on the capacity of the vector after the operation other than capacity() >= size().

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thanks David! I was sure that it was some trick with a side effect. – Dmitriy Kachko Feb 21 '12 at 15:12
  • "*there is no guarantee on the capacity of the vector after the operation other than capacity() >= size()*"... Why is it so? – Nawaz Feb 21 '12 at 15:15
  • 1
    @Nawaz: It just is. The C++ standard explicitly states that *shrink_to_fit* is a non-binding call and states that there is no such guarantee. In this pattern the same occurs, nowhere in the standard it requires the copy not to have the same *capacity* or even more than the original object. The only requirements on `capacity()` is that it must be at least `size()` for obvious reasons. Aside is the fact that this idiom does work in many implementations to reduce the capacity if it is much greater than the size. – David Rodríguez - dribeas Feb 21 '12 at 15:18
  • @DavidRodríguez-dribeas: But there must be some rationale behind *no-guarantee*, don't you think so? – Nawaz Feb 21 '12 at 15:21
  • 1
    @Nawaz: to leave implementations free to use capacities that are convenient for them. For example, and I don't know whether anyone actually does this, you might want to tailor the capacities to just "hit" convenient sizes that waste the least space in your default memory allocator (in practice this is of course rather difficult, since most implementations link against `malloc` without knowing how it works, but someone might come up with a trick). Or you might want the buffer size to always be a multiple of a word, meaning a `vector` would never have capacity 3. – Steve Jessop Feb 21 '12 at 15:23
  • Why not `vector().swap(obj)`? Why pass `obj` into constructor? – sharptooth Feb 21 '12 at 15:29
  • 1
    @Nawaz: for insight, read "guess" ;-). I always assume that anything the standard doesn't require is in order to give implementations freedom, then it's just a matter of trying to think of something that an implementation might plausibly use that freedom for! – Steve Jessop Feb 21 '12 at 15:29
  • @sharptooth: because you want to keep the contents, surely. dribeas has kind of ignored that in his example code, but the questioner doesn't say that the vector is initially empty. – Steve Jessop Feb 21 '12 at 15:29
8

I believe it is a way to "shrink" the vector to a minimal size.

vector<TypeA>(typeAObj) creates a copy of the vector whose reserved size may be smaller than the original.

So swapping a vector with a fresh copy of itself could be a way of freeing some undesired memory.

ereOn
  • 53,676
  • 39
  • 161
  • 238