tags:
- Cpp
Noexcept in C++ (ENG)
noexcept
Specifier (Since C++11)I believe most of us will question what noexpct
keyword does when we start learning "the rule of five". The move constructor and the move assignment operator often have a specifier noexcept
, what for? Why do we always need to make the move constructor noexcept
?
class myClass{
private:
// data members
public:
myClass(){}
~myClass(){}
myClass(const myClass& obj){} // Copy constructor
myClass& operator=(const myClass& obj){} // Copy assignment operator
myClass(myClass&& obj) noexcept {} // Move constructor
myClass& operator=(myClass&& obj) noexcept {} // Move assignment operator
};
If you go to the reference, you will get a simple answer. It's for two purposes: one is for the compiler to generate better code, and the other is for better readability for the user. Every function in C++ is either non-throwing or potentially throwing, and you could specify them explicitly:
void funcA() noexcept {} // non-throwing, which means the function guarantees that it won't throw any exceptions
void funcB() noexcept(true) {} // non-throwing, more explicitly stating that the function guarantees it won't throw any exceptions
void funcA() {} // potentially throwing, meaning the function might throw exceptions
void funcB() noexcept(false) {} // potentially throwing, explicitly stating that the function might throw exceptions
You see, every time you specify noexcept
(noexcept(true)
) , you are simply telling the compiler that the function will not throw errors at run-time, so that the compiler will do its best to optimize the code. Additionally, users can immediately understand that the function will not throw errors by seeing noexcept
.
noexcept
is a promise to the compiler and the runtime that the function will not throw exceptions, and breaking this promise will lead to program termination.
The noexcept
specifier doesn't make the compiler check for potential exceptions at compile-time. It simply means that if an exception occurs within a function marked noexcept
, the program will call std::terminate
and terminate abruptly since the promise is broken.
noexcept
Specifier is often Needed?Then back to our question, why do we need a noexcept
with the move constructor? The real reason lies in STL containers. Functions marked with noexcept
can enable move semantics, while without noexcept
, they fallback to copy operations.
Normally, during a move operation, there should only be ownership transfers (just pointer evaluations). So you cannot really have anything fail happening there.
Is this a must? Well, it's not a must. If you call some potentially throwing function inside a noexcept
specified function, there would be an error. It's easy to understand because you break the promise. Here’s an example illustrating a bad practice:
#include <utility>
class myClass {
private:
// data members
public:
// other member functions
void throwing() {
throw 20;
} // not marked as noexcept
myClass(){}
myClass(myClass&& obj) noexcept(true) {
// data moves
throwing();
} // this would be an error because the throwing function is not noexcept
};
int main(){
myClass obj;
myClass obj2(std::move(obj)); // throw an exception
return 0;
}