I was wondering how is pop_back() implemented so that it has constant complexity? It has to reduce the size by 1 and delete the last element.
Exactly. That's all it has to do. No matter how many elements you have in total.
That's the definition of constant complexity.
Now I saw somewhere that it basically reduces the size by 1 and destroys last element via some iterator/pointer
That's right. The memory is still allocated, but the object living there undergoes logical destruction.
but then why couldn't we implement pop_front() the same way and just redirect our pointer to first element to the next one?
You could! That's how std::deque works, which is why std::deque has a pop_front() with constant complexity.
However, you can't do it while maintaining other cheap operations, because it necessarily causes memory fragmentation after a few times. Memory is allocated in chunks, and vectors need to be allocated in contiguous chunks. Imagine that vectors just "ignored" the first element if you pop_front()'d it — now do that five hundred times. You have unused memory just sitting there forever. Not good! And what if you want to add to the front now? Eventually, unless you want to end up using infinite memory, you'd have to split your memory up into distinct chunks, but that breaks the contiguity guaranteed.
Other containers are designed to give you what you want, but with trade-offs. In particular, std::deque cannot guarantee you contiguous storage. That's how it prevents leaving loads of unused memory lying around all the time.
pop_back()orpop_front()?pop_back()you don't actually have to delete/destroy that last element. You can just decrement the pointer to the last element of your array or decrement the size member (depends how you implemented this). In order topop_front()you would still decrement size, but now you have to shift your elements towards the front, or increment your front pointer. Again depends on your impelementation. The point is you dont have to go out of your way to delete the element. Just stop using the element.vectorthat grows from the middle, enabling fast front operations as well, but the name escapes me. In that case, you can implementpop_frontin roughly the same way.pop_back()certainly does have to destroy the last element. If that element has a destructor it's required that the destructor runs and that the element's lifetime ends.