
on Wed Oct 19 2011, Nevin Liber <nevin-AT-eviloverlord.com> wrote:
On 16 October 2011 06:50, Dave Abrahams <dave@boostpro.com> wrote:
It's still silent, as it isn't the same interface even though the actual call looks the same.
Well, now we could pick nits over the meaning of "same interface."
Are the "Requires" clauses in the Standard part of the interface or not?
Yes, absolutely. But that's just one function. I think I had in mind the total interface of the class, frankly I've forgotten at this point. I see a container that can never store more than N items, for some reasonably small N, as fundamentally, semantically different from one that is highly likely to be able to store any M items you have to deal with, and for which you can't determine a hard limit N ahead of time (no, max_size() doesn't count—that's typically just numeric_limits<size_t>::max()). I grant you that we don't have a very strict way of expressing this difference in a specification, but I think that's just missing. I wouldn't, except in very special circumstances, consider one of those containers to be a suitable replacement for the other, despite the similarity of the specification text. Would you?
I see the answer to that question as something fundamental, not picking nits.
In my point of view they are part of the interface, as they describe when it is legal for me to make a particular function call.
Yes, but I think there's something else in the interface (abstractly) that you may not be accounting for, and that clearly makes these two push_backs different, even if you choose the throwing semantics.
In some sense it is O(1), since N is bounded, but I know what you mean. :-)
There's also the nonthrowing part.
I agree. Then again, the only alternative is not providing swap at all.
Sure. I'm not arguing there's something wrong with the throwing swap in this case; I'm just saying the optimization of "no dynamic allocation, ever" has consequences that ripple through the interface.
I'm not. If you think I am, you've misconstrued me. I'm just trying to understand the design space and represent a point-of-view about handling likely programmer errors that I think is important and overlooked.
When you mess with programmer's expectations, you get programmer errors.
A programmer who doesn't adjust his expectations when replacing something essentially unbounded with something essentially bounded is in for a rude surprise no matter what is done about this particular feature. Sounds like you just don't want to appreciate the point-of-view I'm representing. That's OK, but I don't think I'll follow up on this post in that case.
I do very much dislike the idea of having multiple interfaces for what is essentially the same semantics.
Of course, I'm arguing they are different interfaces, but to address this point, we have multiple interfaces for the same semantics all the time. c.push_back(x) is just c.insert(c.end(), x), c.clear() is just c.erase(c.begin(), c.end()), etc.
I believe you are misunderstanding. In fact, c.insert(c.end(),x) does not (or did not, in C++03) give the same exception-safety guarantees as c.push_back(x).
Just today I had to show someone the swap trick for releasing space in a vector because there is no obvious way to do it in C++03; I don't consider that a good thing.
Sorry, I don't see the relevance.
There are certainly applications where it's knowable statically that the limit can never be violated where one might want to drop in a bounded vector in place of std::vector, and having to spell push_back some other way, as though it were essentially different, is ugly.
I don't think we can resolve this. I see them as different interfaces; you may not. Subtle interface differences lead to subtle bugs.
OK. -- Dave Abrahams BoostPro Computing http://www.boostpro.com