When noexcept?

Posted by user on 12 Dec 2016

In a previous post, we had a look at the new constexpr keyword that has been introduced in C++ 11. Today we'll study another new fancy specifier: noexcept.

C++ is all about specifiers. Every time you add a specifier to a method, the ISO committee makes 1 cent.

Exception specifications in C++ 98

In C++ 03 you could specify that code was not supposed to throw any exception as such:

void my_function() throw()
{
// one does not simply throw
}

Sounds awesome right? Except it's not. When the exception is thrown, the program will terminate. In C++ 98, the stack will be unwound. Not only you don't have any useful feedback, but there is no optimization opportunity for the compiler.

You could also specify that a function throws specific exceptions, but it wasn't so useful: many compilers didn't implement that correctly and the idea is terrible in the first place.

TL;DR: Exceptions specification is useless in C++ 98: don't bother

Exception specification since C++ 11

In C++ 11 two things changed in exception specification:

  1. You either throw or you don't
  2. The compiler doesn't have to unwind the stack before terminating the program
  3. (bonus) You can query the exception safety of an expression at compile-time

Forget about throw, the noexcept specifier is your new friend.

TL;DR: The new exception specification syntax will not interfere with the previous one and is easier to understand. Use it.

The compiler does not check anything

This may be surprising, but this code will compile without an error:

void my_function() noexcept
{
throw std::runtime_error("lolilol");
}

If the compiler were to check functions, this would prevent you from using unspecified code, such as C functions.

TL;DR You are solely responsible of the accuracy of the noexcept specification.

If you throw, your program terminates

What happens if you throw from a noexcept function? Your program terminates, and may or may not unwind the stack.

Terminating program isn't the nicest way to report an error to your user.

You would be surprised that most of C++ code might throw. For example, every time your code allocates memory, there is a possibility for a std::bad_alloc exception to be thrown.

Every function you call, every object you instantiate is an opportunity for an exception to be thrown. noexcept means the code never throws. It is not about probabilities.

TL;DR Tread lightly when using noexcept.

Actual optimization opportunities

When the compiler encounters the noexcept keyword, it will assume your code never throws.

This means it can replace this code:

void my_function() noexcept {}

try
{
my_function();
}
catch(const std::exception & e)
{
std::cerr << e.what() << std::endl;
}

with this code:

void my_function() noexcept {}

my_function();

That's for the most obvious optimization. There are more advanced optimizations enabled with noexcept, such as the conditional moves.

TL;DR noexcept gives real optimization opportunities to the compiler and may thus be worth the effort.

Try not to be stupid

"Faster code with noexcept? Understood! Behold my optimized code!"

void i_am_so_clever(void) noexcept
{
try
{
function_which_throws();
}
catch(...)
{
// ah! ah! to me the optimizations!
}
}

There is no point in writing such code. It's more code, it's actually slower and it's harder to understand. With C++, be straight to the point. There are other languages out there if you like to over-engineer.

TL;DR: If your function throws, let it be and don't try to make it noexcept.

You can infer if a function is noexcept

Imagine this case:

template <typename T, typename Params...>
T awesome_factory(Params... params)
{
return T{params...};
}

You would like to write "if my constructor is noexcept, then I would like my awesome factory to be noexcept.".

First, some more information about what the noexcept specifier actually is. When you write:

void f() noexcept
{
// ...
}

You are implicitly writing:

void f() noexcept(true)
{

}

Lucky you, there is a noexcept operator which returns true when an expression is declared to not throw an exception.

With that knowledge in mind, combining the noexcept specifier with the noexcept operator is a solution to our problem:

template <typename T, typename Params...>
T awesome_factory(Params... params) noexcept(noexcept(T{params...}))
{
return T{params...};
}

TL;DR: Whenever possible, infer if an expression is noexcept with the noexcept operator.

noexcept by default

It is worth noting that these expressions are noexcept by default:

  1. Destructors
  2. Deallocation functions

You could declare them as potentially throwing function (with noexcept(false)), but remember that throwing from a destructor results in undefined behavior.

noexcept is a binding contract

The noexcept not only conveys information to the compiler, it also conveys information to the developer.

That means that not only the compiler will optimize, but most likely the developer using your interface will make assumption based on your specifiers.

You are saying "I am certain this code will never throw exceptions". Once you offer that guarantee, removing it can break a lot of code.

TL;DR: When using noexcept you must not only be certain your code does not throw, but that it will very likely never throw in future implementations. When in doubt, abstain.

When noexcept?

At quasardb we have a rule of thumb for noexcept.

If your expression recursively meets the following criteria:

  • Doesn't explicitly throw an exception
  • Only constructs trivial types or types with noexcept constructors
  • Calls only noexcept or C functions (careful with operators!)
  • Will likely stay as such in the future

Then it is a good noexcept candidate.

When creating an object, it's worth looking at it to see if its constructors and assignment operators can be specified as noexcept:

  1. By induction you make it possible to spread exception safety information throughout your code.
  2. You give real optimization opportunities to the compiler when your objects are used in containers.

Are you looking for a software engineering internship? Look no further! We have available positions!

Topics: cpp, noexcept, Uncategorized