c++stlstdvectorallocation

How to ensure one time memory allocation while creating a std::vector from another container?


When I need to make an std::vector from some elements of another container, e.g. another vector, the safest way to ensure that the new vector will be allocated in memory only once is:

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2;
v2.reserve(amount);
v2.insert(v2.begin(), v1.begin(), v1.begin() + amount);

Meanwhile this code is much longer than very simple constructions from Best way to extract a subvector from a vector like

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2(v1.begin(), v1.begin() + amount);

or

std::vector<int> v1 = { 1, 2, 3, 4, 5, 6 };
const size_t amount = 3;
std::vector<int> v2 = {v1.begin(), v1.begin() + amount};

The questions is: do the latter cases ensure that memory for v2 will be allocated only once or the specific implementation could easy use something like cycle of push_back() or emplace_back() and lead to many container memory reallocation during its creation as a copy?

Is is guaranteed by the standard or at least can I rely on existing implementations that they won't reallocate memory many times?

I am concerned because I work with huge containers and want to make sure this more readable code won't have performance impact.


Solution

  • I found this passage in the C++23 standard:

    24.3.11.2 Constructors [vector.cons]

    template<class InputIterator>
    constexpr vector(InputIterator first, InputIterator last,
    const Allocator& = Allocator());
    
    1. Effects: Constructs a vector equal to the range [first, last), using the specified allocator.
    2. Complexity: Makes only N calls to the copy constructor of T (where N is the distance between first and last) and no reallocations if iterators first and last are of forward, bidirectional, or random access categories. It makes order N calls to the copy constructor of T and order log N reallocations if they are just input iterators.

    That means a conforming implementation will do it as least as efficiently as your manual call to reserve + insert.