Monday December 12, 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.
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
In C++ 11 two things changed in exception specification:
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.
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.
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
.
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.
“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
.
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 thenoexcept
operator.
It is worth noting that these expressions are noexcept
by default:
You could declare them as potentially throwing function (with noexcept(false)
), but remember that throwing from a destructor results in undefined behavior.
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.
At quasardb we have a rule of thumb for noexcept
.
If your expression recursively meets the following criteria:
noexcept
constructorsnoexcept
or C functions (careful with operators!)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
:
Are you looking for a software engineering internship? Look no further! We have available positions!