Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/container/vector/reserve"

From cppreference.com
< cpp‎ | container‎ | vector
(Notes: trash reserve() some more.)
m (Synopsis: +(since C++20) tags)
 
(18 intermediate revisions by 13 users not shown)
Line 1: Line 1:
{{cpp/container/vector/title | reserve}}
+
{{cpp/container/vector/title|reserve}}
 
{{cpp/container/vector/navbar}}
 
{{cpp/container/vector/navbar}}
{{dcl begin}}
+
{{ddcl|notes={{mark constexpr since c++20}}|
{{dcl |  
+
 
void reserve( size_type new_cap );
 
void reserve( size_type new_cap );
 
}}
 
}}
{{dcl end}}
 
  
Increase the capacity of the vector to a value that's greater or equal to {{tt|new_cap}}. If {{tt|new_cap}} is greater than the current {{lc|capacity()}}, new storage is allocated, otherwise the method does nothing.
+
Increase the capacity of the vector (the total number of elements that the vector can hold without requiring reallocation) to a value that's greater or equal to {{c|new_cap}}. If {{c|new_cap}} is greater than the current {{lc|capacity()}}, new storage is allocated, otherwise the function does nothing.
 +
 
 +
{{tt|reserve()}} does not change the size of the vector.
  
 
{{cpp/container/note iterator invalidation|vector|reserve}}
 
{{cpp/container/note iterator invalidation|vector|reserve}}
 +
 +
After a call to {{tt|reserve()}}, insertions will not trigger reallocation unless the insertion would make the size of the vector greater than the value of {{lc|capacity()}}.
  
 
===Parameters===
 
===Parameters===
 
{{par begin}}
 
{{par begin}}
{{par | new_cap | new capacity of the vector }}
+
{{par|new_cap|new capacity of the vector, in number of elements}}
 
{{par hreq}}
 
{{par hreq}}
{{par req concept | T | MoveInsertable}}
+
{{par req insertable|T|MoveInsertable|notes={{mark since c++11}}}}
 
{{par end}}
 
{{par end}}
  
Line 23: Line 25:
 
===Exceptions===
 
===Exceptions===
 
* {{lc|std::length_error}} if {{c|new_cap > max_size()}}.
 
* {{lc|std::length_error}} if {{c|new_cap > max_size()}}.
* any exception thrown by {{tt|Allocator::allocate()}} (typically {{lc|std::bad_alloc}})
+
* Any exception thrown by {{tt|Allocator::allocate()}} (typically {{lc|std::bad_alloc}}).
  
If an exception is thrown, this function has no effect ([[cpp/language/exceptions|strong exception guarantee]]).
+
If an exception is thrown, this function has no effect ([[cpp/language/exceptions#Exception safety|strong exception guarantee]]).
{{rev begin}}
+
{{rrev|since=c++11|
{{rev | since=c++11 |
+
If {{tt|T}}'s move constructor is not {{c/core|noexcept}} and T is not {{named req|CopyInsertable}} into {{c|*this}}, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.
If {{tt|T}}'s move constructor is not {{c|noexcept}} and T is not {{concept|CopyInsertable}} into {{tt|*this}}, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.}}
+
}}
{{rev end}}
+
  
 
===Complexity===
 
===Complexity===
Line 35: Line 36:
  
 
===Notes===
 
===Notes===
Correctly using {{lc|reserve()}} can prevent unnecessary reallocations, but inappropriate uses of {{lc|reserve()}} (for instance, calling it before every {{lc|push_back()}} call) may actually increase the number of reallocations (by causing the capacity to grow linearly rather than exponentially) and result in increased computational complexity and decreased performance. For example, a function that receives an arbitrary vector by reference and appends elements to it should usually ''not'' call {{lc|reserve()}} on the vector, since it does not know of the vector's usage characteristics.
+
Correctly using {{tt|reserve()}} can prevent unnecessary reallocations, but inappropriate uses of {{tt|reserve()}} (for instance, calling it before every {{lc|push_back()}} call) may actually increase the number of reallocations (by causing the capacity to grow linearly rather than exponentially) and result in increased computational complexity and decreased performance. For example, a function that receives an arbitrary vector by reference and appends elements to it should usually ''not'' call {{tt|reserve()}} on the vector, since it does not know of the vector's usage characteristics.
  
When inserting a range, the range version of {{lc|insert()}} is generally preferable as it preserves the correct capacity growth behavior, unlike {{lc|reserve()}} followed by {{lc|push_back()}}.
+
When inserting a range, the range version of {{lc|insert()}} is generally preferable as it preserves the correct capacity growth behavior, unlike {{tt|reserve()}} followed by a series of {{lc|push_back()}}s.
  
{{lc|reserve()}} cannot be used to reduce the capacity of the container, to that end {{lc|shrink_to_fit()}} is provided.
+
{{tt|reserve()}} cannot be used to reduce the capacity of the container; to that end {{lc|shrink_to_fit()}} is provided.
  
 
===Example===
 
===Example===
Line 45: Line 46:
 
|code=
 
|code=
 
#include <cstddef>
 
#include <cstddef>
 +
#include <iostream>
 
#include <new>
 
#include <new>
 
#include <vector>
 
#include <vector>
#include <iostream>
 
  
 
// minimal C++11 allocator with debug output
 
// minimal C++11 allocator with debug output
template <class Tp>
+
template<class Tp>
struct NAlloc {
+
struct NAlloc
 +
{
 
     typedef Tp value_type;
 
     typedef Tp value_type;
 +
   
 
     NAlloc() = default;
 
     NAlloc() = default;
     template <class T> NAlloc(const NAlloc<T>&) {}
+
     template<class T>
     Tp* allocate(std::size_t n) {
+
    NAlloc(const NAlloc<T>&) {}
 +
   
 +
     Tp* allocate(std::size_t n)
 +
    {
 
         n *= sizeof(Tp);
 
         n *= sizeof(Tp);
         std::cout << "allocating " << n << " bytes\n";
+
        Tp* p = static_cast<Tp*>(::operator new(n));
         return static_cast<Tp*>(::operator new(n));
+
         std::cout << "allocating " << n << " bytes @ " << p << '\n';
 +
         return p;
 
     }
 
     }
     void deallocate(Tp* p, std::size_t n) {
+
   
         std::cout << "deallocating " << n*sizeof*p << " bytes\n";
+
     void deallocate(Tp* p, std::size_t n)
 +
    {
 +
         std::cout << "deallocating " << n * sizeof *p << " bytes @ " << p << "\n\n";
 
         ::operator delete(p);
 
         ::operator delete(p);
 
     }
 
     }
 
};
 
};
template <class T, class U>
+
 
 +
template<class T, class U>
 
bool operator==(const NAlloc<T>&, const NAlloc<U>&) { return true; }
 
bool operator==(const NAlloc<T>&, const NAlloc<U>&) { return true; }
template <class T, class U>
+
 
 +
template<class T, class U>
 
bool operator!=(const NAlloc<T>&, const NAlloc<U>&) { return false; }
 
bool operator!=(const NAlloc<T>&, const NAlloc<U>&) { return false; }
  
 
int main()
 
int main()
 
{
 
{
     int sz = 100;
+
     constexpr int max_elements = 32;
 +
   
 
     std::cout << "using reserve: \n";
 
     std::cout << "using reserve: \n";
 
     {
 
     {
 
         std::vector<int, NAlloc<int>> v1;
 
         std::vector<int, NAlloc<int>> v1;
         v1.reserve(sz);
+
         v1.reserve(max_elements); // reserves at least max_elements * sizeof(int) bytes
         for(int n = 0; n < sz; ++n)
+
 
 +
         for (int n = 0; n < max_elements; ++n)
 
             v1.push_back(n);
 
             v1.push_back(n);
 
     }
 
     }
 +
   
 
     std::cout << "not using reserve: \n";
 
     std::cout << "not using reserve: \n";
 
     {
 
     {
 
         std::vector<int, NAlloc<int>> v1;
 
         std::vector<int, NAlloc<int>> v1;
         for(int n = 0; n < sz; ++n)
+
       
 +
         for (int n = 0; n < max_elements; ++n)
 +
        {
 +
            if (v1.size() == v1.capacity())
 +
                std::cout << "size() == capacity() == " << v1.size() << '\n';
 
             v1.push_back(n);
 
             v1.push_back(n);
 +
        }
 
     }
 
     }
 
}
 
}
Line 90: Line 109:
 
|output=
 
|output=
 
using reserve:  
 
using reserve:  
allocating 400 bytes
+
allocating 128 bytes @ 0xa6f840
deallocating 400 bytes
+
deallocating 128 bytes @ 0xa6f840
 +
 
 
not using reserve:  
 
not using reserve:  
allocating 4 bytes
+
size() == capacity() == 0
allocating 8 bytes
+
allocating 4 bytes @ 0xa6f840
deallocating 4 bytes
+
 
allocating 16 bytes
+
size() == capacity() == 1
deallocating 8 bytes
+
allocating 8 bytes @ 0xa6f860
allocating 32 bytes
+
deallocating 4 bytes @ 0xa6f840
deallocating 16 bytes
+
 
allocating 64 bytes
+
size() == capacity() == 2
deallocating 32 bytes
+
allocating 16 bytes @ 0xa6f840
allocating 128 bytes
+
deallocating 8 bytes @ 0xa6f860
deallocating 64 bytes
+
 
allocating 256 bytes
+
size() == capacity() == 4
deallocating 128 bytes
+
allocating 32 bytes @ 0xa6f880
allocating 512 bytes
+
deallocating 16 bytes @ 0xa6f840
deallocating 256 bytes
+
 
deallocating 512 bytes
+
size() == capacity() == 8
 +
allocating 64 bytes @ 0xa6f8b0
 +
deallocating 32 bytes @ 0xa6f880
 +
 
 +
size() == capacity() == 16
 +
allocating 128 bytes @ 0xa6f900
 +
deallocating 64 bytes @ 0xa6f8b0
 +
 
 +
deallocating 128 bytes @ 0xa6f900
 
}}
 
}}
 +
 +
===Defect reports===
 +
{{dr list begin}}
 +
{{dr list item|wg=lwg|dr=329|std=C++98|before=reallocation might be triggered if an insertion<br>makes the size of the vector greater than the size<br>specified in the most recent call to {{tt|reserve()}}|after=only triggers if the size<br>of the vector becomes<br>greater than {{lc|capacity()}}}}
 +
{{dr list item|wg=lwg|dr=2033|std=C++11|before={{tt|T}} was not required to be {{named req|MoveInsertable}}|after=required}}
 +
{{dr list end}}
  
 
===See also===
 
===See also===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc inc | cpp/container/dsc capacity |vector}}
+
{{dsc inc|cpp/container/dsc capacity|vector}}
{{dsc inc | cpp/container/dsc max_size |vector}}
+
{{dsc inc|cpp/container/dsc max_size|vector}}
{{dsc inc | cpp/container/dsc resize |vector}}
+
{{dsc inc|cpp/container/dsc resize|vector}}
{{dsc inc | cpp/container/dsc shrink_to_fit |vector}}
+
{{dsc inc|cpp/container/dsc shrink_to_fit|vector}}
 
{{dsc end}}
 
{{dsc end}}
  
[[cs:cpp/container/vector/reserve]]
+
{{langlinks|cs|de|es|fr|it|ja|pt|ru|zh}}
[[de:cpp/container/vector/reserve]]
+
[[es:cpp/container/vector/reserve]]
+
[[fr:cpp/container/vector/reserve]]
+
[[it:cpp/container/vector/reserve]]
+
[[ja:cpp/container/vector/reserve]]
+
[[pt:cpp/container/vector/reserve]]
+
[[ru:cpp/container/vector/reserve]]
+
[[zh:cpp/container/vector/reserve]]
+

Latest revision as of 15:05, 5 May 2024

 
 
 
 
void reserve( size_type new_cap );
(constexpr since C++20)

Increase the capacity of the vector (the total number of elements that the vector can hold without requiring reallocation) 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 function does nothing.

reserve() does not change the size of the vector.

If new_cap is greater than capacity(), all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise, no iterators or references are invalidated.

After a call to reserve(), insertions will not trigger reallocation unless the insertion would make the size of the vector greater than the value of capacity().

Contents

[edit] Parameters

new_cap - new capacity of the vector, in number of elements
Type requirements
-
T must meet the requirements of MoveInsertable into *this. (since C++11)

[edit] Return value

(none)

[edit] Exceptions

If an exception is thrown, this function has no effect (strong exception guarantee).

If T's move constructor is not noexcept and T is not CopyInsertable into *this, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified.

(since C++11)

[edit] Complexity

At most linear in the size() of the container.

[edit] Notes

Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for instance, calling it before every push_back() call) may actually increase the number of reallocations (by causing the capacity to grow linearly rather than exponentially) and result in increased computational complexity and decreased performance. For example, a function that receives an arbitrary vector by reference and appends elements to it should usually not call reserve() on the vector, since it does not know of the vector's usage characteristics.

When inserting a range, the range version of insert() is generally preferable as it preserves the correct capacity growth behavior, unlike reserve() followed by a series of push_back()s.

reserve() cannot be used to reduce the capacity of the container; to that end shrink_to_fit() is provided.

[edit] Example

#include <cstddef>
#include <iostream>
#include <new>
#include <vector>
 
// minimal C++11 allocator with debug output
template<class Tp>
struct NAlloc
{
    typedef Tp value_type;
 
    NAlloc() = default;
    template<class T>
    NAlloc(const NAlloc<T>&) {}
 
    Tp* allocate(std::size_t n)
    {
        n *= sizeof(Tp);
        Tp* p = static_cast<Tp*>(::operator new(n));
        std::cout << "allocating " << n << " bytes @ " << p << '\n';
        return p;
    }
 
    void deallocate(Tp* p, std::size_t n)
    {
        std::cout << "deallocating " << n * sizeof *p << " bytes @ " << p << "\n\n";
        ::operator delete(p);
    }
};
 
template<class T, class U>
bool operator==(const NAlloc<T>&, const NAlloc<U>&) { return true; }
 
template<class T, class U>
bool operator!=(const NAlloc<T>&, const NAlloc<U>&) { return false; }
 
int main()
{
    constexpr int max_elements = 32;
 
    std::cout << "using reserve: \n";
    {
        std::vector<int, NAlloc<int>> v1;
        v1.reserve(max_elements); // reserves at least max_elements * sizeof(int) bytes
 
        for (int n = 0; n < max_elements; ++n)
            v1.push_back(n);
    }
 
    std::cout << "not using reserve: \n";
    {
        std::vector<int, NAlloc<int>> v1;
 
        for (int n = 0; n < max_elements; ++n)
        {
            if (v1.size() == v1.capacity())
                std::cout << "size() == capacity() == " << v1.size() << '\n';
            v1.push_back(n);
        }
    }
}

Possible output:

using reserve: 
allocating 128 bytes @ 0xa6f840
deallocating 128 bytes @ 0xa6f840
 
not using reserve: 
size() == capacity() == 0
allocating 4 bytes @ 0xa6f840
 
size() == capacity() == 1
allocating 8 bytes @ 0xa6f860
deallocating 4 bytes @ 0xa6f840
 
size() == capacity() == 2
allocating 16 bytes @ 0xa6f840
deallocating 8 bytes @ 0xa6f860
 
size() == capacity() == 4
allocating 32 bytes @ 0xa6f880
deallocating 16 bytes @ 0xa6f840
 
size() == capacity() == 8
allocating 64 bytes @ 0xa6f8b0
deallocating 32 bytes @ 0xa6f880
 
size() == capacity() == 16
allocating 128 bytes @ 0xa6f900
deallocating 64 bytes @ 0xa6f8b0
 
deallocating 128 bytes @ 0xa6f900

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
LWG 329 C++98 reallocation might be triggered if an insertion
makes the size of the vector greater than the size
specified in the most recent call to reserve()
only triggers if the size
of the vector becomes
greater than capacity()
LWG 2033 C++11 T was not required to be MoveInsertable required

[edit] See also

returns the number of elements that can be held in currently allocated storage
(public member function) [edit]
returns the maximum possible number of elements
(public member function) [edit]
changes the number of elements stored
(public member function) [edit]
reduces memory usage by freeing unused memory
(public member function) [edit]