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.

Why 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;
}