pop_back() return value?
Asked Answered
A

8

72

Why doesn't pop_back() have a return value? I have Googled regarding this and found out that it makes it more efficient. Is this the only reason for making it so in the standard?

Audubon answered 26/9, 2012 at 11:6 Comment(0)
R
66

I think there is something related to the fact that copying an instance of the last object could throw an exception. When doing so, you're losing your object, since pop_back() did remove it from your container. Better with a few lines of code:

std::vector<AnyClass> holds = {...} ;
try {
  const AnyClass result = holds.pop_back(); // The copy Ctor throw here!
} catch (...)
{ 
 // Last value lost here. 
}
Returnable answered 26/9, 2012 at 11:12 Comment(8)
+1 T container<T>::pop()cannot be implemented exception safe (gotw.ca/gotw/008.htm)Misshapen
+1 this is the correct answer. It tells us the underlying principle which led to this design of pop_back with no return value.Cullan
You wouldn't copy it, you would move it. That's a whole different ball game.Phlegmatic
Right. I am not really (yet) knowledgeable with c++11. But I guess the answer still holds true. I mean, not with a compiler-provided move ctor, but a user-defined one. Moreover, afaik, I remember reading somewhere that a class may be defined to eludes any move ctor (= delete?). If so, then we fall back in the copy ctor case (if such is provided, otherwise I guess using a vector won't compile).Returnable
@DeadMG move constructors might not be noexcept either, if I'm not mistakenSatang
@DeadMG: move or copy, that depends on the type of value, as not every type is movable, plus move might throw exception as well.Cullan
@Misshapen How can we be sure that a removed object does not throw in destructor? I understand that it shouldn't, but how can we control it?Marcusmarcy
When discussing exception safety you need to consider the kind of safety you guarantee (en.wikipedia.org/wiki/Abrahams_guarantees). As I understand, the convention of avoiding element destruction and copy construction in the same pop method offers the possibility of "strong" exception safety (no data loss). However, STL does not make such guarantees in general, so IMHO they could very well have added a pop method that returns a copy (still guaranteeing "basic" safety, i.e. no leaks). Am I wrong?Cannabin
I
76

Efficiency has little (or nothing, really) to do with it.

This design is the outcome of an important paper by Tom Cargill, published in the 90s, that raised quite a few eyebrows back then. IIRC, in it Cargill showed that it is impossible to design an exception safe stack pop function.

Inge answered 26/9, 2012 at 11:16 Comment(9)
+1 for the reference to Tom Cargill paper "Excpetion handling: a false sense of security". A MUST-READ. :-)Returnable
If I remember correctly then “The C++ Language” or some edition of “Effective C++” actually claimed that it was for efficiency reasons because they hadn’t figured out move construction and NRVO then.Sparing
@Konrad: AFAIR, Meyers even pointed at Cargill's paper. (Come to think of it, I bet that's the actual reason why a copy of it exists, at Pearson's website.)Inge
How can we be sure that a removed object does not throw in destructor? I understand that it shouldn't, but how can we control it?Marcusmarcy
@Mikhail: The same way you can be sure that some function does what the spec says: You need to trust those who wrote the code that they didn't do anything stupid.Inge
@Inge So we guarantee pop_back doesn't throw unless user is stupid?Marcusmarcy
@Mikhail: If destructors throw, you cannot guarantee anything. That's the reason they must not. Again: Destructors must never throw. Period. If you write a dtor that lets escape an exception, you might just as well dereference NULL.Inge
Interesting paper. However I don't immediately see that the paper shows it's impossible to design an exception-safe pop function. For example isn't it possible to copy the last element into a parameter passed by reference, e.g. void Stack::pop(T &oldTop), such that if this copy throws, the stack remains unchanged?Bordure
If NRVO was mandatory would it not be possible to implement this safely?Priory
R
66

I think there is something related to the fact that copying an instance of the last object could throw an exception. When doing so, you're losing your object, since pop_back() did remove it from your container. Better with a few lines of code:

std::vector<AnyClass> holds = {...} ;
try {
  const AnyClass result = holds.pop_back(); // The copy Ctor throw here!
} catch (...)
{ 
 // Last value lost here. 
}
Returnable answered 26/9, 2012 at 11:12 Comment(8)
+1 T container<T>::pop()cannot be implemented exception safe (gotw.ca/gotw/008.htm)Misshapen
+1 this is the correct answer. It tells us the underlying principle which led to this design of pop_back with no return value.Cullan
You wouldn't copy it, you would move it. That's a whole different ball game.Phlegmatic
Right. I am not really (yet) knowledgeable with c++11. But I guess the answer still holds true. I mean, not with a compiler-provided move ctor, but a user-defined one. Moreover, afaik, I remember reading somewhere that a class may be defined to eludes any move ctor (= delete?). If so, then we fall back in the copy ctor case (if such is provided, otherwise I guess using a vector won't compile).Returnable
@DeadMG move constructors might not be noexcept either, if I'm not mistakenSatang
@DeadMG: move or copy, that depends on the type of value, as not every type is movable, plus move might throw exception as well.Cullan
@Misshapen How can we be sure that a removed object does not throw in destructor? I understand that it shouldn't, but how can we control it?Marcusmarcy
When discussing exception safety you need to consider the kind of safety you guarantee (en.wikipedia.org/wiki/Abrahams_guarantees). As I understand, the convention of avoiding element destruction and copy construction in the same pop method offers the possibility of "strong" exception safety (no data loss). However, STL does not make such guarantees in general, so IMHO they could very well have added a pop method that returns a copy (still guaranteeing "basic" safety, i.e. no leaks). Am I wrong?Cannabin
C
20

It's because of the Command-query separation principle.

Collator answered 26/9, 2012 at 11:13 Comment(0)
A
8

Efficiency is one thing. Another reason for pop_back() not returning an element is exception safety.
If the pop() function returned the value, and an exception is thrown by the copy constructor, you may not be able to guarantee that the container is in the same state as it was before calling pop().

You can find more infos in Herb Sutters books about exceptions. I think this topic is covered here. But I am not sure.

Agist answered 26/9, 2012 at 11:14 Comment(1)
The problem isn't that the container is changed, the problem is that the object might be removed, but you haven't gotten it back — so it's lost.Inge
L
7

The reason is not so much efficiency as exception safety. The container class can be used to store any kind of objects. It would be impossible to implement pop_back() in an exception safe manner if the function would return the object after deleting it from the container, because returning the value of the object involves copy construction.

This is the actual implementation of vector::pop_back() in GNU C++ standard library:

  void
  pop_back()
  {
    --this->_M_impl._M_finish;
    this->_M_impl.destroy(this->_M_impl._M_finish);
  }

This is what it would look like should it return the last element in the end:

  value_type
  pop_back()
  {
    value_type save = back();
    --this->_M_impl._M_finish;
    this->_M_impl.destroy(this->_M_impl._M_finish);
    return save;
  }

This involves two copy constructions, at the save = back() statement and when returning a copy of the object. There are no guarantees that the return expression won't throw an exception after the element has been destroyed from the container.

Lautrec answered 26/9, 2012 at 11:17 Comment(0)
O
5

Well, how many reasons there have to be?

This avoids potentially expensive copying of the object when you just want to remove it from the container. C++ has the philosophy of not paying for what you don't need.

Obstructionist answered 26/9, 2012 at 11:11 Comment(5)
I believe this is wrong. See my comment to Ravindra's answer.Inge
well, I am quite sure that exception related issue is likey one of the reasons, but I am also quite sure that this is another. I have yet to see any evidence that this design has nothing to do with performance issue (as move semantics is quite a new thing) and I haven't claimed there isn't another (OK, my first sentence might sound like that, but it was meant to clarify why one reason wouldn't be enough). Sutter claims in "Exceptional C++" that "here's one reason to do this: It avoids weakening exception safety.".Obstructionist
"I have yet to see any evidence that this design has nothing to do with performance issue." You also have yet to see any evidence that this design has nothing to do with bananas. So? It's you who made a claim ("This is about performance!"), and it's me who challenged that. So it's you who need to back up the claim.Inge
ok, you got me working there :) Stroustrup says in 'C++ prog. lang' [16.3.5]: It just pops, and if we want to know what was on the top of the stack before pop, we must look. This happens not to be my favorite style of stack, but it's arguably more efficient and it's the standard. OTOH, Josuttis book also refers to Cargill paper. My conclusion is that performance is one of the reasons, while I don't claim that it is the only one. However, upon reading all the references I would also agree that exception-safety might have been more important factor in deciding this way.Obstructionist
this is the best answer, why not avoid a needless copy?Angio
A
0

In computer programming, orthogonality means that operations change just one thing without affecting others.

pop_back() does just one thing, it does not copy, hence it is orthogonal.

Angio answered 25/12, 2021 at 13:4 Comment(0)
P
-1

Why would it return the value? You can always access the value at any time prior to popping it off- there is no need for pop_back to provide this functionality.

Phlegmatic answered 26/9, 2012 at 11:11 Comment(3)
-1. It doesn't answer the question. There is a rationale why pop_back doesn't return value, and this post doesn't talk about that rationale.Cullan
The fact that there is no reason for it to exist is an excellent reason for it to not exist, and that does answer the question.Phlegmatic
@DeadMG: I believe that there's a good usability case pro pop_back() returning a value. Traditionally, that's what a stack's pop function does. No, the reason is purely for exception safety.Inge

© 2022 - 2024 — McMap. All rights reserved.