Introduction
In C++, functions potentially throw exceptions. However, there are many functions, such as getter functions or move constructors, that we know “will absolutely not throw an exception.”
The noexcept specifier, introduced in C++11, tells the compiler that a function does not throw exceptions by adding it after the function declaration. This allows the compiler to perform more aggressive optimizations, which can improve performance.
Also, noexcept serves as important documentation for programmers to clarify the specification of a function.
Prerequisite: What is C++11?
C++11 is a major update to the C++ language formalized in 2011. Since noexcept was introduced in C++11, you need a compiler that supports C++11 or later to use it.
How to Use noexcept
1. noexcept: Unconditionally Not Throwing Exceptions
When you add noexcept (or noexcept(true)), the function is marked as not throwing exceptions. If an exception is thrown from a function specified with noexcept, the program immediately calls std::terminate and ends abnormally.
#include <iostream>
#include <string>
#include <utility> // move
using namespace std;
class Widget {
private:
string name_;
public:
// This getter function does not contain processing that throws exceptions
const string& getName() const noexcept {
return name_;
}
// If you make move constructors and move assignment operators noexcept,
// STL containers handle them more efficiently
Widget(Widget&& other) noexcept {
name_ = move(other.name_);
}
};
Rule of Thumb: You should actively add noexcept to functions that generally should not throw exceptions, such as destructors, move constructors/move assignment operators, and swap functions.
2. noexcept(condition): Conditionally Not Throwing Exceptions
By putting a conditional expression evaluated at compile time inside the parentheses of noexcept, you can decide whether to enable the noexcept specification based on the condition.
This is very powerful, especially in template programming, when you want to inherit the noexcept property of a function belonging to a template argument type.
C++
class Resource {
public:
void process() const { /* May throw exception */ }
};
class SafeResource {
public:
void process() const noexcept { /* Does not throw exception */ }
};
// Template function
template <typename T>
void execute_process(const T& item) noexcept(noexcept(item.process())) {
item.process();
}
int main() {
// is_nothrow_invocable_v is a C++17 feature
cout << boolalpha;
cout << "execute_process<Resource> is noexcept? -> "
<< noexcept(execute_process(Resource{})) << endl; // -> false
cout << "execute_process<SafeResource> is noexcept? -> "
<< noexcept(execute_process(SafeResource{})) << endl; // -> true
return 0;
}
Explanation:
noexcept(item.process()): Thenoexceptoperator returns a boolean value indicating thenoexceptstatus of the expression passed as an argument.- If
itemis of typeResource:item.process()is notnoexcept, so it returnsfalse. - If
itemis of typeSafeResource:item.process()isnoexcept, so it returnstrue.
- If
void execute_process(...) noexcept(...): Thenoexceptstatus of theexecute_processfunction itself becomes linked to thenoexceptstatus of the internalitem.process()call.
Summary
In this article, I explained the C++11 noexcept specifier.
noexcept: Explicitly states that a function does not throw exceptions. This helps compiler optimization and may lead to performance improvements.- In principle, you should add
noexceptto destructors and move operations. noexcept(condition): Appliesnoexceptconditionally and propagates the noexcept status in templates.
By using noexcept appropriately, you provide important information about the function’s behavior to both the compiler and other programmers reading the code, allowing you to write safer and more efficient code.
