views:

266

answers:

4
  • Does vector::operator= change vector capacity? If so, how?
  • Does vector's copy constructor copy capacity?

I looked through documentation but could not find a specific answer. Is it implementation dependent?

A: 

It is implementation dependent. Most in practice shrink the vectors to the minimum size.

vectros
that seems wrong, because it would invalidate previous `reserve ()`
aaa
@aaa the standard just says "It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the size specified in the most recent call to reserve()" . Assignment of another vector isn't an insertion, so a reallocation may happen during an assignment, which however must rellocate to the same, or to a higher `capacity` than what you specified previously (which makes sense, since the vector assigned from may contain more elements).
Johannes Schaub - litb
the capacity of the vector being copied remains the same. the vector capacity of the copy is implementation dependent, and its capacity is usually shrunk to the minimum capacity.
vectros
However, it's not allowed to shrink. In fact, that's why the `vector<int>().swap(v);` idiom exists.
Johannes Schaub - litb
to clarify, is your comment regarding assignment or copy constructor?@litb I meant invalidate in sense make smaller. I get what you saying
aaa
swap is a special case. swap is not a copy. copied vectors can have any capacity, as it is not explicitly mandated by the standard.
vectros
+6  A: 

All you're guaranteed is that:

  1. The vector has enough capacity to store its elements. (Obviously.)
  2. The vector won't get a new capacity until it's current capacity is full.*

So how much extra or little an implementation wants to put is up to the implementation. I think most will make capacity match size, when copying, but it cannot lower capacity. (Because of number 2 above; reallocating while there's enough room is not allowed.)

* Mostly. See Charles' comments below.

GMan
Actually, you're not quite guaranteed (2). In current C++ you are guaranteed that no reallocation occurs after a call to `reserve` until an insertion would take the size beyond the value of the previous call to reserve. Before a call to reserve, or after a call to reserve when the size is between the value of the previous call to reserve and the capacity the implementation is allowed to reallocate early if it so chooses. Whether this is desirable, I don't know.
Charles Bailey
In C++0x the requirement has changed and after a call to `reserve` no reallocation will happen until an insertion moves the size of the container beyond the `capacity` (note, not the size previously passed to `reserve` any more). This means that you can `reserve`, query the `capacity` and know with certainty exactly when the next reallocation will happen.
Charles Bailey
@Charles: Good to know. :) I will mention in the laziest way possible.
GMan
You are guaranteed #2 through "The total number of elements that the vector can hold without requiring reallocation." [23.2.4.2/1 C++03]. (But you're not guaranteed anything about the destination vector's capacity after assignment other than it is no less than before the assignment, and it is of course no less than the new size of the vector.)
Roger Pate
"...without _requiring_ reallocation..." but that's my point. The standard doesn't mandate that a vector shall not reallocate even if it has spare capacity and doesn't strictly need to. It wouldn't break the interface contract to do this unless the client had actually called `reserve`.
Charles Bailey
The only (sensible??) use that I can think of for this is that if it attempts to reallocate early but fails, `capacity()` would still tell you how many items you can expect to store in the container even if it was only giving you an upper bound on the number of items you can insert before it next attempts reallocation.
Charles Bailey
@Roger Pate: Yes, it's good that they fixed the two calls to `reserve` issue, but note that when 23.2.4.3 hasn't changed. "Causes reallocation if the new size is greater than the old capacity" is still there and still doesn't mean "Reallocation is not caused if the new size is less than or equal to the capacity". To guarantee this behaviour you have to call `reserve` at least once. You can `reserve(0)` in C++0x, in C++0x you would need to `reserve(capacity())` whenever you cause the capacity to change.
Charles Bailey
@Charles: 23.2.4.3/1 hasn't changed because it doesn't need to; 23.1/11 states "Unless otherwise specified .., invoking a container member function .. shall not invalidate iterators to .. that container."
Roger Pate
Someone was confused by what I was pointing out, so explicitly: 23.2.4.3/1 specifies when reallocation occurs, if reallocation does not occur under that (or another) requirement, then it *must not* occur, per 23.1/11.
Roger Pate
@Roger: I think you've read a little more into the wording than is actually there. 23.2.4.3 clearly indicates that `insert` (and functions that call `insert`) may cause reallocation and specifies a condition where it must cause reallocation. 23.2.4.2 also places restrictions on reallocation. I don't believe (and clearly the submitter of DR329 also agrees) that it's disallowed to reallocate when the new size is less than the old capacity.
Charles Bailey
@Charles: You were right that DR329 itself was off the mark (which is why I've deleted that comment), but I respectfully say I believe it's the reverse and you're reading more into it. 23.1/11 says invalidation (which reallocation requires) must not occur unless specified, so where is it specified that op= reallocates if the new size is not more than the old capacity?
Roger Pate
@Roger: I hope that the standard doesn't state the op= reallocates if new size <= old capacity at all, as it may be required to not reallocate!
Charles Bailey
@Roger: Please note that 23.1/11 doesn't forbid re-allocation. It says "must not invalidate iterators to, or change the values of,...". Unlike the requirements for swap given above, it explicitly omits "references to". Of course, if vector does reallocate in a function that isn't explicitly allowed to invalidate iterators then it can't use raw pointers as iterators.
Charles Bailey
@Charles: "Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence." [23.2.4.2/5] I do not interpret that as "Reallocations performed by reserve() invalidate..", so any reallocation must invalidate everything listed.
Roger Pate
@Roger: All I'm saying is that vector can move things around any time it chooses, even given 23.1/11, so long as it keeps iterators valid. When it's allowed a "reallocation" it can invalidate everything. It doesn't have to as the client can't rely on it or they will get UB. Just reading 23.2.4.3/1 again, I can't read that first sentence in any way other than leaving the door open for the converse not to be true. Otherwise I'd expect to see "and only if". `reserve` will give you that guarantee if you call it.
Charles Bailey
@Charles, To me the issue is clear now: Since it's not specified that reallocation happens if the new size is between the old argument of reserve and capacity(), iterators must not be invalidated. Since that happens when reallocation happens, reallocation is forbidden. It's only specified to reallocate if the new size is greater than the capacity.
Johannes Schaub - litb
@litb: Are you relying on 23.1/11 for the conclusion that iterators must not be invalidated? If so, I'm not convinced but I'll agree to disagree. I think that the section on insert explicitly allows for reallocation (assuming no reserve) even if the new size is less then the original capacity - it's just not prohibited. If you take this as true - and I admit this is probably the cause for our disagreement - then you can't use 23.1/11 to say otherwise as it would be a circular argument.
Charles Bailey
+1  A: 

As i wrote before, the copy need not - and usually DOES NOT - retain the capacity of the original vector.

gcc version 4.1.1

$ cat vt.cpp
#include <vector>
#include <iostream>
int main() {
   std::vector<int> v1;
   v1.reserve(50000);
   std::vector<int> v2 = v1;
   std::cout << v1.capacity() << std::endl;
   std::cout << v2.capacity() << std::endl;
   return 0;
}

$ g++ vt.cpp -o vt && ./vt
50000
0

$ cat v2.cpp
#include <vector>
#include <iostream>
int main() {
   std::vector<int> v1;
   v1.reserve(50000);
   std::vector<int> v2;
   v2 = v1;
   std::cout << v1.capacity() << std::endl;
   std::cout << v2.capacity() << std::endl;
   return 0;
}

$ g++ v2.cpp -o v2 && ./v2
50000
0
vectros
That's not using `operator=`.
Johannes Schaub - litb
@vectros: If you had `std::vector<int> v2; v2.reserve(v1.size()*2); v2 = v1;`, then it is not allowed to shrink it's capacity while copying the contents of `v1`.
GMan
You're both wrong.
vectros
as for not using operator=() - i realize it was using the copy ctor. it's not relevant. it behaves the same with the copy ctor as it does with operator=().
vectros
they wrote `v2.reserve(size_t)` , rather than `v1.reserve`
aaa
okay, if you explicitly reserved the capacity in v2, v2 will respect that. but that was not the original question.
vectros
GMan
i thought you wrote v1.reserve(). not sure why you introduced v2.reserve() into this discussion, as it was not part of the original question. anyway, the code above answers the question.
vectros
@vectros: Sure it was part of the question. The question has to do with capacity, and reserve deals directly with that.
GMan
+2  A: 

Does vector::operator= change vector capacity? If so, how?

It might change capacity. This happens only if the previous capacity was too small to hold the new size. If so, the new capacity is at least equal to the new size, but could be a larger value.

Does copy constructor copy capacity?

Per Table 65 Container requirements in C++03, X u (a); and X u = a; are both equivalent to X u; u = a;. This makes the copy ctor identical to the op= case, after default constructing the vector.

Roger Pate