26

I am trying to fix some code that uses vectors a lot and there are some loops that look like this:

for (int t=0;t<T;t++){    
    std::vector<double> vect;
    for (int i=0;i<MAX;i++){
        double value;
        vect.push_back(value);
    }
    /*....*/
}

I know more or less how to improve this by reusing the same vector for the outer iterations, but while doing this I found that when calling std::vector::clear "the vector capacity is not guaranteed to change", while I was actually hoping that the capacity would be guaranteed to not change. Maybe I am just misunderstanding what is written on cplusplus.com. However, my question is:

How can I clear a vector without changing its capacity?

Should I call reserve after clear to make sure the capacity will be the same?

PS: just to be clear, I want to rewrite above code to

std::vector<double> vect;
vect.reserve(MAX);
for (int t=0;t<T;t++){    
    for (int i=0;i<MAX;i++){
        double value;
        vect.push_back(value);
    }
    /*....*/
    vect.clear();
}

ie. I still want to fill it via push_back and I am worried about the clear() changing the capacity of the vector.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • 1
    There's related discussion here. The consensus seems to be that resizing a vector SMALLER must not affecte the capacity. http://stackoverflow.com/questions/1624803/does-resizing-a-vector-invalidate-iterators – Roddy May 04 '16 at 13:36
  • 3
    A better answer was in http://stackoverflow.com/a/18467916 – Cubbi May 04 '16 at 17:33

5 Answers5

17

cppreference said explicitly that the capacity of the vector is unchanged.

from cppreference (bold emphases it is own me):

void clear();

Removes all elements from the container. Invalidates any references, pointers, or iterators referring to contained elements. May invalidate any past-the-end iterators.
Leaves the capacity() of the vector unchanged.

EDIT

As pointed out by Dmitry Kuznetsov in the comment, the standard does not mention the capacity:

expression: a.clear()

return type: void
Assertion/note pre-/post-condition: Destroys all elements in a. Invalidates all references, pointers, and iterators referring to the elements of a and may invalidate the past-the-end iterator.

post: a.empty() returns true.

Complexity: Linear.

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
Alessandro Teruzzi
  • 3,918
  • 1
  • 27
  • 41
3

Yes, call reserve() after clear(). In the worst case, you'll have a single deallocation/allocation taking place, which would have negligible impact in case of a vector of PODs.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Doesn't matter that it's PODs. There's no object construction taking place when you call `clear()` – Roddy May 04 '16 at 13:32
2

You can store value returned from capacity() and use reserve() method.

It's not guaranteed to give you the exact same capacity:

Increase the capacity of the container to a value that's greater or equal to new_cap. If new_cap is greater than the current capacity(), new storage is allocated, otherwise the method does nothing.

int main()
{
    vector<int> vec { 1,2,3,4,5 };
    size_t cap = vec.capacity(); 
    vec.clear();
    vec.reserve(cap);

    cout << "vec capacity: " << vec.capacity() << '\n';
}
Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • where did you take the quote? In particular I am interested whether it is really guaranteed to do nothing in case the requested capacity is smaller than the current capacity. Not that it would matter so much, but sometimes details do matter – 463035818_is_not_an_ai May 04 '16 at 12:36
  • ah thanks, actually I was a bit confused, because in my quote I mixed up cppreference with cplusplus... – 463035818_is_not_an_ai May 04 '16 at 12:39
-1

Another way would be to do a do a swap trick...

std::vector<double> vect = {0.1, 0.2, 0.3}
{
    std::vector<double> swapVector;
    swapVector.reserve(vect.capacity());
    vect.swap(swapVector); // swapVector now has the elements of vect
                           // vect has only reserved elements
} // swapVector will be destroyed (including the elements that was in vect)
std::cout << vect.capacity();
A.Fagrell
  • 1,052
  • 8
  • 21
  • "vect will be cleared when it goes out of scope" ?? I dont understand – 463035818_is_not_an_ai May 04 '16 at 12:52
  • @tobi303 swapVector is created on the stack and has been swapped with the elements in vect. As soon as the function goes out of scope swapVector will be destroyed and it's elements... let me edit my answer – A.Fagrell May 04 '16 at 13:05
  • I see plenty of downside and no advantages to this approach. You're forcing a deallocation and reallocation (although in the other order), whichg is exactly what the OP wants to avoid, – Roddy May 04 '16 at 13:14
  • @Roddy yeah I guess you're right. The only benefit would be (if that would be needed) is that vect is "emptied" before the elements are deallocated... – A.Fagrell May 04 '16 at 13:39
-2

Calling clear will deallocate (not necessarily, but depending on the implementation), then calling resize will result in a costly reallocation. The obvious answer would be to just iterate though the vector and to set the results to 0 / NULL:

for(double& d : vect)
{
    d = 0;
    //d = NULL, depending on datatype
}

This will just "clear" the vector without a costly reallocation. Of couse, the performance of this procedure is highly dependent on the number of elements - this is just the easy way out.

tubberd
  • 540
  • 5
  • 15