C++ has got some keywords, some of them handled differently in different contexts, depending on situation.
Today, we will talk about noexcept.
What the hell is noexcept(noexcept())?
noexcept is a specifier which tells to compiler that "I will not throw any kind of exceptions those cannot be caught inside of function body, they leak outside!".
It's a kind of guarantee to compiler, so compiler can do some tricky optimizations.
noexcept can be in form of function specifier or operator. [except.spec/1]
noexcept as an operator
noexcept as an operator, checks given expression is potentially-throwing or not. Yes. It just checks that.
Buuut. Given expression will not be evaluated. Let's take a look:
#include <iostream>
// std::cout
#include <iomanip>
// std::boolalpha
class X {
public:
X operator+(int) const noexcept {
return *this;
}
X operator-(float) const {
return *this;
}
};
int main() {
auto& Out = std::cout << std::boolalpha; // reference to std::ostream, used for shortening std::cout << std::boolalpha, nothing fancy
int a { 0 }; // direct initialized.
Out << noexcept(a = 1) << '\n'; // `a` will stay same. result is `true`. it satisfies all conditions of
[except.spec/6] since it's primitive type.
X b; // default initialized
Out << noexcept(b + 1) << '\n'; // compiler will look at the operator+(X, int), which is marked as noexcept(true).
Out << noexcept(b - 1.f) << '\n'; // compiler will look at the operator-(X, float), which is marked as noexcept(false).
[except.spec/3]
return 0;
}
Alright. That's cool. What about noexcept as a function specifier?
noexcept as a function specifier marks your function as noexcept(true) or noexcept(false) depending on given or not boolean argument.
by default, functions are marked as noexcept(false).
[except.spec/3]
If argument is not provided -as just plain `noexcept`- then function will be marked as noexcept(true).
If provided, then; given expression must be *compile time evaluatable, so expression is constant expression* -since noexcept will perform compile time checks on function-,
and given expression will be contextually converted to bool
([except.spec/2]).
if expression is not constant expression or expression cannot convertable to bool; compile time error will be occurred.
Let's look at possible ways to use noexcept function specifier,
normally noexcept specifier includes noexcept operator too; but we are explicitly saying "function specifier" to resolve any ambiguity:
void foo() noexcept {} // same as noexcept(true), foo() is marked as noexcept.
void bar() {} // same as noexcept(false), bar() is marked as potentially throwing.
void baz() noexcept(sizeof(bool) == 1) {} // we can't say anything directly, since it's implementation defined.
[expr.sizeof/1]
Okay. So what is noexcept(noexcept()) then?
So, we know that there is function specifier and operator form of noexcept. Inner noexcept() is an operator
which looks if given argument is potentially throwing any exceptions.
Outer noexcept is exception specifier that takes optionally an expression and marks function or lambda as noexcept(true) or noexcept(false).
So, we can say that: "If given expression is noexcept, then I am noexcept too; or vice versa.".
Let's look at an example:
void foo() noexcept {} // foo() is marked as noexcept
void bar() noexcept(noexcept(foo())) {} // if foo() is noexcept, then bar() is noexcept too.
void baz() noexcept(noexcept(foo))) {} // foo is always returns true, since it's not a function call, an identifier,
// which is not potentially throwing. satisfies all conditions.
[except.spec/6]
Notes:
* There may be floating point exceptions, but most (probably none) of them are not directly about C++ exceptions. Built-in operators will not throw exception
since [except.spec/6] conditions are satisfied directly.
* Used C++23 N4950 final working draft.
* If there is any wrong information or something can be added extra, please go to repository, and create new pull request. Thank you!