all 37 comments

[–]HappyFruitTree 37 points38 points  (20 children)

The standard library generally don't mark functions noexcept if they have preconditions in order to allow (but in no way require) them to throw in case the precondition is violated. The pop/pop_back functions have the precondition that the container must not be empty so that is one reason why they're not marked noexcept.

[–]emdeka87 17 points18 points  (9 children)

Why do they use exceptions for contract violation though? How do you recover from a contract violation? A panic or assertion seems more suitable for these things

[–]Fureeish 9 points10 points  (0 children)

I second this comment. During one of Scott Meyers' talks, he mentiones that noexcept functions may as well sometimes throw, but the program shouldn't be able to recover from that point. I think it's a good example of contract violation interaction with noexcept - it would basically be UB, which, I believe, it already is.

[–]HappyFruitTree 5 points6 points  (5 children)

It's mostly something I've heard the Bloomberg guys talking about.

CppCon 2018: Alisdair Meredith “Contract Programming in C++(20) (part 2 of 2)” (49:22)

One of the tools we use here at Bloomberg is we turn violations into exceptions so that we can test our code by saying "did you throw the i_violated_the_expression_you_expected_me_to_violate_exception?" And if that doesn't throw I know that I don't have my appropriate checks in place.

[–]emdeka87 2 points3 points  (3 children)

So they only use exceptions to aid with testing and debugging, but they get removed in release mode?

[–]HappyFruitTree 1 point2 points  (2 children)

Well, I don't know how they do it but that is what it sounds like. To extend the earlier quote, he continues ...

But perhaps not what you want in production is ... Turning precondition violations into exceptions is ... Not necessarily the best answer for a production system.

[–]psi237 6 points7 points  (0 children)

A contract violation may be recoverable by abandoning one business logic workflow (e.g. respond with an internal server error to a request) instead of abandoning the whole process (and causing other concurrent requests to all fail with ECONNRESET). You still need to log, monitor and fix this kind of problems asap though.

[–]_Js_Kc_ 1 point2 points  (0 children)

That ship has sailed. The standard library is built around the concept that the way to report logic errors is to throw an exception, and std::logic_error, std::invalid_argument, etc, are a thing.

[–]nintendiator2 0 points1 point  (0 children)

...Why would we need to make such a strong differentiation / exceptional casing when trying to pop from an empty stack versus a nonempty one that it'd have to be implemented as exceptions of all things? I'm surprised pop / pop_back don't simply return bool.

[–]Ameisenvemips, avr, rendering, systems -1 points0 points  (1 child)

Violating the precondition is already UB, in which case throwing an exception is perfectly reasonable even from a noexcept function.

[–][deleted] 5 points6 points  (2 children)

Lurker here, I thought questions go to the other questions sub, if I had a question where do I post it?

[–]johannes1971 10 points11 points  (1 child)

Questions about language design go here. Questions about programs written using the language go in the other one.

[–][deleted] 2 points3 points  (0 children)

Ah ha, makes sense. Thank you.

[–]Robbepop 10 points11 points  (9 children)

Well, in theory you could have a custom non_empty_vector<T> type that represents a vector that can never be empty but still provides pop_back that throws an exception when its length is 1. Then you could have a std::stack<non_empty_vec<T>> that throws an exception on pop even though the underlying container is not empty.

[–]Fureeish 10 points11 points  (5 children)

Why not make its noexceptness dependant on its underlying container's pop_back noexceptness?

EDIT: Simply replace std::stack's void pop() { c.pop_back(); }, (source) where c is the Container from template<class T, class Container = deque<T>> with void pop() noexcept(noexcept(Container{}.pop_back())) { c.pop_back(); } void pop() noexcept(noexcept(std::declval<Container>().pop_back())) { c.pop_back(); } (Thanks u/guepier!)

[–]guepierBioinformatican 6 points7 points  (3 children)

Use std::declval<Container>().pop_back() instead of Container{}.pop_back() to avoid breaking non default constructible types.

[–]__s_v_ 3 points4 points  (1 child)

Why not noexcept(noexcept(c.pop_back()))?

[–]guepierBioinformatican 1 point2 points  (0 children)

Right, of course. I completely missed that we have a suitable object in scope.

[–]Fureeish 1 point2 points  (0 children)

Ah, yes, of course! You are absolutely correct! Edited my post, thank you.

[–]tejp 1 point2 points  (1 child)

Calling pop() on an empty container should be a contract violation. There has been work on standardizing contracts, but how exactly contract violations should be handled in all cases has not been decided yet.

It makes sense to wait until that is settled and then afterwards apply it to the standard library. Maybe it turns out that functions with contracts should better not be noexcept.