On Thursday, June 27, 2013 11:53:39 AM UTC-7, Nevin ":-)" Liber wrote:
The pop_back() won't get called until after the move assignment into the "final" destination is complete (so exceptions shouldn't be any more of a problem than they usually are),
Except with the current two liner, it is obvious in user code that user left the back() element in a weird state.
Exceptions should not be leaving things in "weird" states, especially in a move constructor, or you are already in enough trouble. If your move constructor can throw an exception after _partially_ moving, you have potentially serious problems all through the standard library and elsewhere in your code. The exception problems with the OP suggestion are avoided with mine entirely.
The big downside is that you must predeclare/initialize the output variable, which also means you can't really use auto (I can think of ways the language could make this nicer, but that's an entirely different discussion).
In other words, it is still a pessimization.
I'm not sure what you mean by that.
The original question was is about turning a two-liner into a one-liner. This is way more complicated for very little gain and no practical experience. I would vote strongly against such a proposal, if it were to come up.
No, it isn't. The existing code is not two lines. The original proposal is about popping values, getting the back value, and safely handling the case of an empty container, so that following logic can work independent of whether the container had been empty or not. That's 5 lines at best right now, 6 with some formatting styles.
value v{};
if (!c.empty()) {
v = c.back();
c.pop_back();
}
With the OP suggestion, that turns into a nicely concise:
auto v = c.pop_back({});
Compared to the version I suggested:
value v;
c.try_pop_back(v);
So yes, my suggestion is ever so slightly more verbose than the one in the original suggestion, but it's still more concise than what we have now, It also has the advantage that in cases where you actually care if the container was empty and a defaulted value is not sufficient (say, a stack of integers, or std::string, etc.) it still works. Extrapolating to data structures like set or map, a try_erase(key) is also a big improvement:
// now
auto it = map.find(key);
if (it != map.end())
map.erase(key);
use(it->second);
// try_erase
value v;
if (map.try_erase(key, v))
use(v);
Going a bit down that rabbit hole, various alternative versions of unordered_map and unordered_set which can contain significant performances improvements over the stdlib implementations (by having different conditions, e.g. allowing insert and erase to invalidate iterators and requiring that value move assignment is noexcept) once again cannot be implemented using the old-style interface (since you can't find an iterator, erase its key, and then reuse that iterator), and the original proposal doesn't work for types which have no "nil" state (and you may not want or just can't use something like std::optional<> for values). Not supporting the generic and reusable-everywhere interface just makes C++ more complicated to use.
Plus, not that I enjoy hitting the same nail repeatedly, but consistency with concurrent containers would be nice.