If there are two vectors, say v1 with big capacity and v2 with small
capacity, and v2 is copied to v1 (v1 = v2), the big capacity in v1 is
kept after the assignment; this makes sense,
It doesn't to me.
After an assignment I expect the assigned-to vector to have the same value and state as the vector is assigned from. Why should I incur and have to drag around excess capacity.
From a quick scan of the standard I am not sure that the standard guarantees that capacity is kept constant across assignment from a smaller vector. (It would be kept across an invocation of vector::assign(...)
, so that may be the intent.)
If I care about memory efficiency, I have to call vector::shrink_to_fit()
after assignment in many cases, if the assignment doesn't do this for me.
Copy and swap has shrink-to-fit semantics. Actually it was the usual C++98 idiom for shrink-to-fit of standard containers.
since next v1.push_back() calls don't have to force new reallocations
(in other words: freeing already available memory, then reallocating
it to grow the vector doesn't make much sense).
True, but that depends on your usage patterns. If you assign vectors and then continue to add to them, keeping any pre-existing capacity makes sense. If you assign a vector after you have built its contents, you may not want to keep excess capacity allocated.
But, if the same assignment is done with the class having the vector
as data member, the behavior is different, and after the assignment
the bigger capacity is not kept.
True, if you do copy and swap in that class. Doing that will also copy and swap contained vectors, and as mentioned above that is a way to achieve shrink to fit.
If the copy-and-swap idiom is not used, and copy operator= and move
operator= are implemented separately, then the behavior is as expected
(as for ordinary non-member vectors).
As discussed above: it is debatable whether that behavior is as expected.
But if it fits your usage patterns, i.e. if you want to continue to grow a vector after it was assigned from another that may have been smaller than the prior value, then you can indeed gain some efficiency by using something that does not drop existing excess capacity (for example vector::assign
).
Why is that? Should we not follow copy-and-swap idiom and instead
implement operator=(const X& other) (copy op=) and operator=(X&&
other) (move op=) separately for optimum performance?
As discussed, if it fits your usage pattern and if performance of that assign and append sequence is critical, then you could indeed consider not using swap and copy for assignment. The main purpose of swap and copy is minimal implementation (avoidance of duplicated code) and strong exception safety.
If you opt for a different implementation for maximum performance, you'll have to take care of exception safety yourself and you'll pay a price in code complexity.