With regards to "complexity" you always have to look at the big picture, not just an individual case.
Let's look at growing with push_back()
first. If you grow from an empty container to one of a large size N, the implementation will do a number of reallocations. Each time it does, the complexity will be O(M)
for the size it is at the time, but the rest of the iterations it will be constant time.
In actual fact the complexity will be the sum of a geometric progression. Let's say it reallocates to a capacity double its size, and let's say it starts at 16.
So the complexity total for those that reallocate will be:
16 + 32 + 64 + 128 + ... + N/2
(assumption that N is a power of 2) which as you know will sum to
N-16.
Add in all the ones for the single ones where no reallocation takes place, and your grand sum is close to 2N
, which is therefore O(N)
for all the N
insertions, and therefore constant time per insertion in the big picture, even if this particular one might not be.
Now the case in point. Let's assume that we start with N
and do a large series of pop_back()
calls. Assuming it does exactly the same in reverse, it will have the same complexity.
Q.E.D.
Of course there are more issues here. For example, a pop_back
and erase
may not throw, i.e. leak an exception, and a reallocation even to a smaller buffer will first need to allocate more memory to move the data into before releasing the old, so an exception could occur doing this. However the implementation could simply "try" the reallocation to a smaller buffer and if an error occurs, catch it then revert back to a simple "logical" change. Of course it does mean your system is reaching full capacity, and that reducing the sizes of your strings isn't helping.
The likelihood is that, whilst this is open to implementation, most implementations will never reallocate in a pop_back. The only time something like this might happen is if the string is implemented with an internal member buffer for small strings and this length is reached, in which case there is no danger is moving all the data into it, and freeing the "free-store" memory. That cannot throw.