void std::vector::pop_back

Started by
9 comments, last by phresnel 15 years, 3 months ago
Why the void return type. I got this as a question in a test and it has been bugging me. Is it because there would be no meaningful return type, as it will always succeed even if empty, and to return a bool saying an element has been removed is pretty meaningless.
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Advertisement
The only meaningful return value in my oppinion would be the object being popped, so that you can do myObj = myObjVector.pop_back() for example (grab the last one and remove it in one go). The problem with this, though, is that you must return by value. You cannot return it by reference since the object was just removed from the vector.
There would be no meaningful data to return because the object would have been destructed from the pop_back operation if it was not a pointer. In that event, you can assume the object was no longer needed, so if you did return something, it would be creating a new copy of the object which should have been "removed".

Consider the output of the following program:
#include <vector>#include <iostream>class MyClass{	int filler;public:	MyClass() 		: filler(0)	{		std::cout << "ctor" << std::endl;	}	MyClass(const MyClass & rhs)	{		filler = rhs.filler;		std::cout << "copy ctr" << std::endl;	}	~MyClass()	{		std::cout << "dtor" << std::endl;	}};int main(int argc, char * argv[]){	std::vector<MyClass> vec;	MyClass c;	std::cout << "- 1 --" << std::endl;	vec.push_back(c);	std::cout << "- 2 --" << std::endl;	vec.pop_back();	std::cout << "---" << std::endl;	return 0;}


ctor- 1 --copy ctrcopy ctrdtor- 2 --dtor---dtorPress any key to continue . . .


The first "ctor" is from c being constructed in main. The next two "copy ctor" are generated inside the push_back function, which emphasizes the possible dangers of using an incorrect class design. The following dtor is one temporary object that is cleaned up inside push_back again. The following dtor is from the pop_back call which destructs the object that was stored.

If it were to return what the last value was, it would be calling the ctor again and depending on your class design, that would be a bad thing since you now have a new object created. The last dtor is from c being destructed, as expected.

Besides those ramifications, the design of the standard C++ library is that of, one function, one purpose (more or less). If you need to last item of a vector, you can use an iterator, (vec.end() - 1), since .end() returns the element one after the last item. If you want to remove the last item, you can simply use pop_back, which does the same thing as if you were to call, vec.erase(vec.end() - 1). In both those cases, the actions of either getting or removing are "separate".

[edit]Pretty much reexplaining what Brother Bob posted ^^
Indeed, the most logical return type would be to return the data you just popped (like a stack). But I guess that's what std::stack is for.

To return a boolean that specifies if the vector was successfully popped would work, but there are other ways to check that. And I'm guessing the STL team wanted to make it as straightforward as possible.

So all that remains is to return void (e.g. nothing).
It threw me somewhat I admit, I think I was blinded by the simplicity of the question. I never questioned it before and then the question just made me think I was missing something.

Edit: Oh and returning the object removed would cause issues I would think, as has been mentioned, and also what would you return if the vector was indeed empty.
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Quote:Original post by HomerSp
Indeed, the most logical return type would be to return the data you just popped (like a stack). But I guess that's what std::stack is for.


Just for reference, the stack, queue, and vector containers all are designed similarly in that, the push/pop family methods are void return types. You have to use the top/front/iterator or index operators, respectively, to actually get any data.
pop_back() is guaranteed to not throw, but that guarantee can't be hold up with non-void pop:
Great articles, thanks Phresnel.

So in essence it is simplicity of design for exception safety's sake, not that I actually think much could be gained from having pop return anything; you can easily get the last element (safely), if required, before popping. My interpretation (now) is that it is the KISS approach.

It was a bit of a curve ball, especially for the level I thought the role in question was.
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Other languages/libraries often return a (copy) of the element that was removed when "popping" -- If memory serves, this was avoided in the SC++L for "speed" (un-necessary copying -- e.g. if you don't need the returned element -- could be wasteful).
Quote:Original post by phresnel
pop_back() is guaranteed to not throw, but that guarantee can't be hold up with non-void pop:


Great articles there, phresnel. I've been starting to compile my bits and pieces of utility code into usable libraries, and even though it is in Ruby, I run into these same exception issues. Great stuff there. Thanks!

Rates++.

This topic is closed to new replies.

Advertisement