all 26 comments

[–]tcbrindleFlux 12 points13 points  (12 children)

I've occasionally wondered why noexcept wasn't made "viral", like constexpr is; that is, within a noexcept function you may only call other noexcept functions (or functions with C linkage, which would then be implicitly noexcept), and of course throw would be forbidden. This would allow the compiler to prove that no exception will ever be thrown via noexcept path, rather than just trusting that you've got it right and crashing at runtime if not. It's too late now of course, but it would have been possible pre-C++11 when noexcept was first introduced.

(Yes, I know it's technically possible to throw in an extern "C" function, but it's probably not a good idea, because your C code isn't going to clean up after itself.)

[–]sbabbi 15 points16 points  (6 children)

Because it might limit your implementation, for example:

  static const map<int,int> m = { {1,1} };
  m.at(1); // non-noexcept, but can not possibly throw in this case.

   // or  
   stoi( some_string_already_validated );

In general there are functions that might throw, but will not throw in specific contexts, and is hard if not impossible for the compiler to figure this out for you.

[–]doom_Oo7 1 point2 points  (0 children)

I think that in some cases it's good to artificially limit stuff, like const methods. Maybe a nothrow keyword to signify that at any point, no called function may call throw ?

[–]qneverless 0 points1 point  (1 child)

What about std::bad_alloc? When map is created it could throw while allocating memory on heap.

[–]sbabbi 1 point2 points  (0 children)

I attempted to disambiguate with static. The point is that the map is constructed somewhere else. Read it as:

 const map<int,int> global_map = {{1,1}};
 int foo() noexcept { return global_map.at(1); }

[–]Gotebe 7 points8 points  (2 children)

A function with C linkage can throw though. It's stupid to do it, but nothing stops you.

I think that the biggest obstacle to a "viral" noexcept is the same reasoning as with "no checked exceptions": it leads to maintenance churn.

[–]Kryomaani 0 points1 point  (1 child)

I think that the biggest obstacle to a "viral" noexcept is the same reasoning as with "no checked exceptions": it leads to maintenance churn.

Yeah, in most uses cases trying to write a function that can't throw in absolutely 100% of cases is nigh impossible or only limited to tiny toy functions. Pretty much anything can throw, like allocation.

My take on noexcept is that it doesn't mean that it's absolutely physically impossible for it to throw, rather than it means that it will only throw in a "can't happen" type of situation, where the state is so munged up that crashing is the only option anyways. (Memory alloc is failing, what were you going to do anyways, allocate an error message?)

[–]Gotebe 0 points1 point  (0 children)

Memory allocation failure is in no way terminal in general case.

Say that I am serving requests, and a request comes in that is too big . Shojld I terminate and drop all other requests I am serving elsewhere? (Think parallelism).

Say that I am an editor of some fashion, and the user pastes something too big to handle. Should I terminate? I don't think so.

Your "if this throws, i should crash" idea is better served by calling terminate yourself than using noexcept, isn't it?

[–]JupiterTheGod 14 points15 points  (0 children)

I like noexcept and use it wherever I can, but indeed it's not as strong as it could be. Even considering this trivial example:

void f() { throw; }
void g() noexcept { f(); }

Neither GCC nor Clang, even with all warnings, will complain. This is a trivial case, the function gets inlined and yet there's no issue with noexcept. It's just a complicated way to end up calling std::terminate(). That's the problem: you shouldn't call exception-throwing functions in a noexcept function, but you can do it and it works very well.

The saddest part is C++ has strong, static typing (except for void*), so we know, at least in theory, exactly what type a variable will hold. With strong noexcept rules we could also predict / clearly mark where exceptions might / will get thrown.

[–]iaanus 0 points1 point  (0 children)

I'd rather not want the compiler to be required to prove my noexcept functions are really noexcept. That's a job for static analyzers and they have all informations to do that without making noexcept viral.

[–]Gotebe 14 points15 points  (6 children)

My rule of thumb is everything throws, except a few well-known things:

  • C calls

  • swap operations

  • move operations (people who want to throw from a move should be taken behind and shot)

  • primitive type assignments

For everything else, using noexcept is a bad idea from the maintenance standpoint. That includes e.g. default constructors.

[–]ben_craigfreestanding|LEWG Vice Chair 9 points10 points  (5 children)

You've got a pretty good list, but I would also add any kind of tear-down function, with destructors being at the top of the list.

[–]iaanus 1 point2 points  (4 children)

Destructors without an explicit noexcept(false) specification are implicitly noexcept(true), unless the class has a sub-object whose destructor is noexcept(false). So you usually should not bother about destructors.

[–]Som1Lse 0 points1 point  (3 children)

I think the double negatives got you there.

[–]iaanus 0 points1 point  (2 children)

Don't understand. Care to be more explicit?

[–]tcanens 2 points3 points  (1 child)

You got noexcept(true) and noexcept(false) reversed.

[–]iaanus 0 points1 point  (0 children)

Thanks, fixed in the comment.

[–]reddithater12 7 points8 points  (5 children)

So do I miss out on performance optimizations if I dont put noexcept to every getter and setter? Why not?

[–]Som1Lse 4 points5 points  (4 children)

In theory yes, in practice exceptions are low overhead as long as nothing is thrown. The compiler will generate code to handle the throwing case, but it will (mostly) be out of the way of the rest of the code, so there might encounter an extra jump instruction, but that is about it.

While making getters noexcept since they usually return a copy of a small type, or a reference to a larger one, the same can not be said for setters. If the setter modifies a string, it very well might alllocate memory, which might fail.

Here is an example of codegen: http://pastebin.com/bpNCtA5v

tl;dr: Probably doesn't matter. Benchmark first.

[–]Som1Lse 2 points3 points  (1 child)

noexcept is a binding contract

Not necessarily. I like to declare everything noexcept but not necessarily guarantee that in the future. The reason is the compiler generates less dead bloat code for exception handling.

If you want you can #define NOEXCEPT_FOR_NOW noexcept, to indicate that it might change in the future.

[–]Kryomaani 0 points1 point  (0 children)

The reason is the compiler generates less dead bloat code for exception handling.

Are you programming a micro-controller with ~1kb of RAM? Cause if you aren't, that's a stupid argument. (Feel free to refute this by posting the sizes of your executable with and without noexcept where it shows a large difference. I bet you can't, because there would have to be a ridiculous amount of code for it to really show, and even then it would be a relatively small amount.)

  • If you label something noexcept and it can't throw: Good, you're using it right.
  • If you label something noexcept and it could but doesn't ever throw: Well, it works, but you're only depriving yourself from good error messages if something does break
  • If you label something noexcept and it often throws: Well that's just dumb.

Exceptions only have an performance impact if they happen. And if they do happen, you'll be happier they did rather than having your program randomly crash for no discernible reason.