ChatGPT解决这个技术问题 Extra ChatGPT

When should I really use noexcept?

The noexcept keyword can be appropriately applied to many function signatures, but I am unsure as to when I should consider using it in practice. Based on what I have read so far, the last-minute addition of noexcept seems to address some important issues that arise when move constructors throw. However, I am still unable to provide satisfactory answers to some practical questions that led me to read more about noexcept in the first place.

There are many examples of functions that I know will never throw, but for which the compiler cannot determine so on its own. Should I append noexcept to the function declaration in all such cases? Having to think about whether or not I need to append noexcept after every function declaration would greatly reduce programmer productivity (and frankly, would be a pain in the neck). For which situations should I be more careful about the use of noexcept, and for which situations can I get away with the implied noexcept(false)? When can I realistically expect to observe a performance improvement after using noexcept? In particular, give an example of code for which a C++ compiler is able to generate better machine code after the addition of noexcept. Personally, I care about noexcept because of the increased freedom provided to the compiler to safely apply certain kinds of optimizations. Do modern compilers take advantage of noexcept in this way? If not, can I expect some of them to do so in the near future?

Code that uses move_if_nothrow (or whatchamacallit) will see a performance improvement if there's a noexcept move ctor.

P
Peter Mortensen

I think it is too early to give a "best practices" answer for this as there hasn't been enough time to use it in practice. If this was asked about throw specifiers right after they came out then the answers would be very different to now.

Having to think about whether or not I need to append noexcept after every function declaration would greatly reduce programmer productivity (and frankly, would be a pain).

Well, then use it when it's obvious that the function will never throw.

When can I realistically expect to observe a performance improvement after using noexcept? [...] Personally, I care about noexcept because of the increased freedom provided to the compiler to safely apply certain kinds of optimizations.

It seems like the biggest optimization gains are from user optimizations, not compiler ones due to the possibility of checking noexcept and overloading on it. Most compilers follow a no-penalty-if-you-don't-throw exception handling method, so I doubt it would change much (or anything) on the machine code level of your code, although perhaps reduce the binary size by removing the handling code.

Using noexcept in the big four (constructors, assignment, not destructors as they're already noexcept) will likely cause the best improvements as noexcept checks are 'common' in template code such as in std containers. For instance, std::vector won't use your class's move unless it's marked noexcept (or the compiler can deduce it otherwise).


I think the std::terminate trick still obeys the Zero-Cost model. That is, it just so happens that the range of instructions within the noexcept functions is mapped to call std::terminate if throw is used instead of the stack unwinder. I therefore doubt it has more overhead that regular exception tracking.
@Klaim See this: stackoverflow.com/a/10128180/964135 Actually it just has to be non-throwing, but the noexcept guarantees this.
"If a noexcept function throws then std::terminate is called which seems like it would involve a small amount of overhead"… No, this should be implemented by not generating exception tables for such a function, which the exception dispatcher should catch and then bail out.
@Pubby C++ exception handling is usually done with no overhead except jump tables which map potentially throwing call site addresses to handler entry points. Removing those tables is as close as it gets to removing exception handling completely. The only difference is executable file size. Probably not worth mentioning anything.
"Well then use it when it's obvious that the function will never throw." I disagree. noexcept is part of the function's interface; you should not add it just because your current implementation happens not to throw. I am not sure about the right answer to this question, but I am quite confident that how your function happens to behave today has nothing to do with it...
P
Peter Mortensen

As I keep repeating these days: semantics first.

Adding noexcept, noexcept(true) and noexcept(false) is first and foremost about semantics. It only incidentally condition a number of possible optimizations.

As a programmer reading code, the presence of noexcept is akin to that of const: it helps me better grok what may or may not happen. Therefore, it is worthwhile spending some time thinking about whether or not you know if the function will throw. For a reminder, any kind of dynamic memory allocation may throw.

Okay, now on to the possible optimizations.

The most obvious optimizations are actually performed in the libraries. C++11 provides a number of traits that allows knowing whether a function is noexcept or not, and the Standard Library implementation themselves will use those traits to favor noexcept operations on the user-defined objects they manipulate, if possible. Such as move semantics.

The compiler may only shave a bit of fat (perhaps) from the exception handling data, because it has to take into account the fact that you may have lied. If a function marked noexcept does throw, then std::terminate is called.

These semantics were chosen for two reasons:

immediately benefiting from noexcept even when dependencies do not use it already (backward compatibility)

allowing the specification of noexcept when calling functions that may theoretically throw, but are not expected to for the given arguments


Maybe I'm naive, but I would imagine a function that invokes only noexcept functions wouldn't need to do anything special, because any exceptions that might arise trigger terminate before they get to this level. This differs greatly from having to deal with and propagate a bad_alloc exception.
Yes, it is possible to define noexcept in way as you suggest, but that would be a really unusable feature. Many function can throw if certain conditions aren't hold, and you couldn't call them even if you know the conditions are met. For example any function which may throw std::invalid_argument.
@MatthieuM. A bit late for a reply, but nevertheless. Functions marked noexcept can call other functions that can throw, the promise is that this function will not emit an exception i.e. they just have to handle the exception themselves!
I upvoted this answer a long time ago, but having read and thought about it some more, I have a comment/question. "Move semantics" is the only example I have ever seen anybody give where noexcept is clearly helpful / a good idea. I am starting to think move construction, move assignment, and swap are the only cases there are... Do you know of any others?
@Nemo: In the Standard library, it is possibly the only one, however it exhibits a principle which can be reused elsewhere. A move operation is an operation that temporarily put some state in "limbo", and only when it is noexcept may someone use it confidently on a piece of data that could be accessed afterward. I could see this idea being used elsewhere, but the Standard library is rather thin in C++ and it is only used to optimize out copies of elements I think.
P
Peter Mortensen

This actually does make a (potentially) huge difference to the optimizer in the compiler. Compilers have actually had this feature for years via the empty throw() statement after a function definition, as well as propriety extensions. I can assure you that modern compilers do take advantage of this knowledge to generate better code.

Almost every optimization in the compiler uses something called a "flow graph" of a function to reason about what is legal. A flow graph consists of what are generally called "blocks" of the function (areas of code that have a single entrance and a single exit) and edges between the blocks to indicate where flow can jump to. Noexcept alters the flow graph.

You asked for a specific example. Consider this code:

void foo(int x) {
    try {
        bar();
        x = 5;
        // Other stuff which doesn't modify x, but might throw
    } catch(...) {
        // Don't modify x
    }

    baz(x); // Or other statement using x
}

The flow graph for this function is different if bar is labeled noexcept (there is no way for execution to jump between the end of bar and the catch statement). When labeled as noexcept, the compiler is certain the value of x is 5 during the baz function - the x=5 block is said to "dominate" the baz(x) block without the edge from bar() to the catch statement.

It can then do something called "constant propagation" to generate more efficient code. Here if baz is inlined, the statements using x might also contain constants and then what used to be a runtime evaluation can be turned into a compile-time evaluation, etc.

Anyway, the short answer: noexcept lets the compiler generate a tighter flow graph, and the flow graph is used to reason about all sorts of common compiler optimizations. To a compiler, user annotations of this nature are awesome. The compiler will try to figure this stuff out, but it usually can't (the function in question might be in another object file not visible to the compiler or transitively use some function which is not visible), or when it does, there is some trivial exception which might be thrown that you're not even aware of, so it can't implicitly label it as noexcept (allocating memory might throw bad_alloc, for example).


Does this actually make a difference in practice? The example is contrived because nothing before x = 5 can throw. If that part of the try block served any purpose the reasoning wouldn't hold.
I'd say it does make a real difference in optimizing functions which contain try/catch blocks. The example I gave although contrived, isn't exhaustive. The larger point is that noexcept (like the throw() statement before it) helps the compile generate a smaller flow graph (less edges, less blocks) which is a fundamental part of many optimizations it then does.
How can compiler recognize that the code can throw exception? Is and access to array considered as possible exception?
@qub1n If the compiler can see the body of the function it can look for explicit throw statements, or other things like new that can throw. If the compiler cannot see the body then it must rely on the presence or absence of noexcept. Plain array access does not typically generate exceptions (C++ doesn't have bounds checking) so no, array access wouldn't alone cause the compiler to think a function throws exceptions. (Out-of-bound access is UB, not a guaranteed exception.)
@cdhowie "it must rely on the presence or absence of noexcept" or presence of throw() in pre-noexcept C++
A
Andrzej

noexcept can dramatically improve performance of some operations. This does not happen at the level of generating machine code by the compiler, but by selecting the most effective algorithm: as others mentioned, you do this selection using function std::move_if_noexcept. For instance, the growth of std::vector (e.g., when we call reserve) must provide a strong exception-safety guarantee. If it knows that T's move constructor doesn't throw, it can just move every element. Otherwise it must copy all Ts. This has been described in detail in this post.


Addendum: That means if you define move constructors or move assignment operators add noexcept to them (if applicable)! Implicitly defined move member functions have noexcept added to them automatically (if applicable).
N
Nicol Bolas

When can I realistically except to observe a performance improvement after using noexcept? In particular, give an example of code for which a C++ compiler is able to generate better machine code after the addition of noexcept.

Um, never? Is never a time? Never.

noexcept is for compiler performance optimizations in the same way that const is for compiler performance optimizations. That is, almost never.

noexcept is primarily used to allow "you" to detect at compile-time if a function can throw an exception. Remember: most compilers don't emit special code for exceptions unless it actually throws something. So noexcept is not a matter of giving the compiler hints about how to optimize a function so much as giving you hints about how to use a function.

Templates like move_if_noexcept will detect if the move constructor is defined with noexcept and will return a const& instead of a && of the type if it is not. It's a way of saying to move if it is very safe to do so.

In general, you should use noexcept when you think it will actually be useful to do so. Some code will take different paths if is_nothrow_constructible is true for that type. If you're using code that will do that, then feel free to noexcept appropriate constructors.

In short: use it for move constructors and similar constructs, but don't feel like you have to go nuts with it.


Strictly, move_if_noexcept won't return a copy, it will return a const lvalue-reference rather than an rvalue-reference. In general that will cause the caller to make a copy instead of a move, but move_if_noexcept isn't doing the copy. Otherwise, great explanation.
+1 Jonathan. Resizing a vector, for example, will move the objects instead of copying them if the move constructor is noexcept. So that "never" is not true.
I mean, the compiler will generate better code in that situation. OP is asking for an example for which the compiler is able to generate a more optimized application. This seems to be the case(even though it's not a compiler optimization).
@mfontanini: The compiler only generates better code because the compiler is forced to compile a different codepath. It only works because std::vector is written to force the compiler to compile different code. It's not about the compiler detecting something; it's about user code detecting something.
The thing is, I can't seem to find "compiler optimization" in the quote at the begining of your answer. As @ChristianRau said, it the compiler generates a more efficient code, it doesn't matter what's the origin of that optimization. After all, the compiler is generating a more efficient code, isn't it? PS: I never said it was a compiler optimization, I even said "It's not a compiler optimization".
P
Peter Mortensen

In Bjarne's words (The C++ Programming Language, 4th Edition, page 366):

Where termination is an acceptable response, an uncaught exception will achieve that because it turns into a call of terminate() (§13.5.2.5). Also, a noexcept specifier (§13.5.1.1) can make that desire explicit. Successful fault-tolerant systems are multilevel. Each level copes with as many errors as it can without getting too contorted and leaves the rest to higher levels. Exceptions support that view. Furthermore, terminate() supports this view by providing an escape if the exception-handling mechanism itself is corrupted or if it has been incompletely used, thus leaving exceptions uncaught. Similarly, noexcept provides a simple escape for errors where trying to recover seems infeasible. double compute(double x) noexcept; { string s = "Courtney and Anya"; vector tmp(10); // ... } The vector constructor may fail to acquire memory for its ten doubles and throw a std::bad_alloc. In that case, the program terminates. It terminates unconditionally by invoking std::terminate() (§30.4.1.3). It does not invoke destructors from calling functions. It is implementation-defined whether destructors from scopes between the throw and the noexcept (e.g., for s in compute()) are invoked. The program is just about to terminate, so we should not depend on any object anyway. By adding a noexcept specifier, we indicate that our code was not written to cope with a throw.


This sounds to me as if I actually should add a noexcept every time, except I explicitly want to take care of exceptions. Let's be real, most of the exceptions are so improbable and/or so fatal that rescuing is hardly reasonable or possible. E.g. in the quoted example, if allocation fails, the application will hardly be able to continue working correctly.
This may be a naive question, but why is the focus on vector<double> tmp(10);? Can't the string instance creation in the line above equally throw if there's not enough memory for it?
C
Community

There are many examples of functions that I know will never throw, but for which the compiler cannot determine so on its own. Should I append noexcept to the function declaration in all such cases?

noexcept is tricky, as it is part of the functions interface. Especially, if you are writing a library, your client code can depend on the noexcept property. It can be difficult to change it later, as you might break existing code. That might be less of a concern when you are implementing code that is only used by your application.

If you have a function that cannot throw, ask yourself whether it will like stay noexcept or would that restrict future implementations? For example, you might want to introduce error checking of illegal arguments by throwing exceptions (e.g., for unit tests), or you might depend on other library code that could change its exception specification. In that case, it is safer to be conservative and omit noexcept.

On the other hand, if you are confident that the function should never throw and it is correct that it is part of the specification, you should declare it noexcept. However, keep in mind that the compiler will not be able to detect violations of noexcept if your implementation changes.

For which situations should I be more careful about the use of noexcept, and for which situations can I get away with the implied noexcept(false)?

There are four classes of functions that should you should concentrate on because they will likely have the biggest impact:

move operations (move assignment operator and move constructors) swap operations memory deallocators (operator delete, operator delete[]) destructors (though these are implicitly noexcept(true) unless you make them noexcept(false))

These functions should generally be noexcept, and it is most likely that library implementations can make use of the noexcept property. For example, std::vector can use non-throwing move operations without sacrificing strong exception guarantees. Otherwise, it will have to fall back to copying elements (as it did in C++98).

This kind of optimization is on the algorithmic level and does not rely on compiler optimizations. It can have a significant impact, especially if the elements are expensive to copy.

When can I realistically expect to observe a performance improvement after using noexcept? In particular, give an example of code for which a C++ compiler is able to generate better machine code after the addition of noexcept.

The advantage of noexcept against no exception specification or throw() is that the standard allows the compilers more freedom when it comes to stack unwinding. Even in the throw() case, the compiler has to completely unwind the stack (and it has to do it in the exact reverse order of the object constructions).

In the noexcept case, on the other hand, it is not required to do that. There is no requirement that the stack has to be unwound (but the compiler is still allowed to do it). That freedom allows further code optimization as it lowers the overhead of always being able to unwind the stack.

The related question about noexcept, stack unwinding and performance goes into more details about the overhead when stack unwinding is required.

I also recommend Scott Meyers book "Effective Modern C++", "Item 14: Declare functions noexcept if they won't emit exceptions" for further reading.


Still it would make much more sense if exceptions were implemented in C++ like in Java, where you mark method that may throw with throws keyword instead of noexcept negative. I just can not get some of C++ design choices...
They named it noexcept because throw was already taken. Simply put throw can be used almost they way you mention, except they botched the design of it so it became almost useless - detrimental even. But we are stuck with it now since removing it would be a breaking change with little benefit. So noexcept is basically throw_v2.
How is throw not useful?
@curiousguy "throw" itself (for throwing exceptions) is useful, but "throw" as an exception specifier has been deprecated and in C++17 even removed. For reasons why exception specifiers are not useful, see this question: stackoverflow.com/questions/88573/…
@PhilippClaßen The throw() exception specifier did not provide the same guarantee as nothrow?
R
Raedwald

There are many examples of functions that I know will never throw, but for which the compiler cannot determine so on its own. Should I append noexcept to the function declaration in all such cases?

When you say "I know [they] will never throw", you mean by examining the implementation of the function you know that the function will not throw. I think that approach is inside out.

It is better to consider whether a function may throw exceptions to be part of the design of the function: as important as the argument list and whether a method is a mutator (... const). Declaring that "this function never throws exceptions" is a constraint on the implementation. Omitting it does not mean the function might throw exceptions; it means that the current version of the function and all future versions may throw exceptions. It is a constraint that makes the implementation harder. But some methods must have the constraint to be practically useful; most importantly, so they can be called from destructors, but also for implementation of "roll-back" code in methods that provide the strong exception guarantee.


This is the best answer by far. You are making a guarantee to users of your method, which is another way of saying you are constraining your implementation forever (sans breaking-change). Thanks for the enlightening perspective.
d
drbombe

Here is a simple example to illustrate when it could really matter.

#include <iostream>
#include <vector>
using namespace std;
class A{
 public:
  A(int){cout << "A(int)" << endl;}
  A(const A&){cout << "A(const A&)" << endl;}
  A(const A&&) noexcept {cout << "A(const A&&)" << endl;}
  ~A(){cout << "~S()" << endl;}
};
int main() {
  vector<A> a;
  cout << a.capacity() << endl;
  a.emplace_back(1);
  cout << a.capacity() << endl;
  a.emplace_back(2);
  cout << a.capacity() << endl;
  return 0;
}

Here is the output

0
A(int)
1
A(int)
A(const A&&)
~S()
2
~S()
~S()

If we remove the noexcept in the move constructor, here is the output

0
A(int)
1
A(int)
A(const A&)
~S()
2
~S()
~S()

The key difference is A(const A&&) vs A(const A&&). In the second case, it has to copy all the values using the copy constructor. VERY INEFFICIENT!!


Looks like you are using this example: youtube.com/watch?v=AG_63_edgUg My question is: Will using -fno-exceptions compiler option trigger the same performance benefit as marking the move constructor noexcept?
I just tested it with GCC and clang trunk and it seems like marking the function noexcept is still required even if using -fno-execptions for the move constructor to be preferred.