Skip to content

Commit

Permalink
[except.spec] Exception specifications are function declarators
Browse files Browse the repository at this point in the history
  • Loading branch information
AlisdairM committed Oct 16, 2024
1 parent c7dc821 commit 886de55
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 266 deletions.
266 changes: 266 additions & 0 deletions source/declarations.tex
Original file line number Diff line number Diff line change
Expand Up @@ -4422,6 +4422,272 @@
\indextext{declaration!default argument|)}%
\indextext{declarator!meaning of|)}

\rSec3[except.spec]{Exception specifications}%
\indextext{exception specification|(}

\pnum
The predicate indicating whether a function cannot exit via an exception
is called the \defn{exception specification} of the function.
If the predicate is false,
the function has a
\indextext{exception specification!potentially-throwing}%
\defnx{potentially-throwing exception specification}%
{potentially-throwing!exception specification},
otherwise it has a
\indextext{exception specification!non-throwing}%
\defn{non-throwing exception specification}.
The exception specification is either defined implicitly,
or defined explicitly
by using a \grammarterm{noexcept-specifier}
as a suffix of a function declarator\iref{dcl.fct}.

\begin{bnf}
\nontermdef{noexcept-specifier}\br
\keyword{noexcept} \terminal{(} constant-expression \terminal{)}\br
\keyword{noexcept}\br
\end{bnf}

\pnum
\indextext{exception specification!noexcept!constant expression and}%
In a \grammarterm{noexcept-specifier}, the \grammarterm{constant-expression},
if supplied, shall be a contextually converted constant expression
of type \keyword{bool}\iref{expr.const};
that constant expression is the exception specification of
the function type in which the \grammarterm{noexcept-specifier} appears.
A \tcode{(} token that follows \keyword{noexcept} is part of the
\grammarterm{noexcept-specifier} and does not commence an
initializer\iref{dcl.init}.
The \grammarterm{noexcept-specifier} \keyword{noexcept}
without a \grammarterm{constant-expression}
is
equivalent to the \grammarterm{noexcept-specifier}
\tcode{\keyword{noexcept}(\keyword{true})}.
\begin{example}
\begin{codeblock}
void f() noexcept(sizeof(char[2])); // error: narrowing conversion of value 2 to type \keyword{bool}
void g() noexcept(sizeof(char)); // OK, conversion of value 1 to type \keyword{bool} is non-narrowing
\end{codeblock}
\end{example}

\pnum
If a declaration of a function
does not have a \grammarterm{noexcept-specifier},
the declaration has a potentially throwing exception specification
unless it is a destructor or a deallocation function
or is defaulted on its first declaration,
in which cases the exception specification
is as specified below
and no other declaration for that function
shall have a \grammarterm{noexcept-specifier}.
In an explicit instantiation\iref{temp.explicit}
a \grammarterm{noexcept-specifier} may be specified,
but is not required.
If a \grammarterm{noexcept-specifier} is specified
in an explicit instantiation,
the exception specification shall be the same as
the exception specification of all other declarations of that function.
A diagnostic is required only if the
exception specifications are not the same
within a single translation unit.

\pnum
\indextext{exception specification!virtual function and}%
If a virtual function has a
non-throwing exception specification,
all declarations, including the definition, of any function
that overrides that virtual function in any derived class
shall have a non-throwing
exception specification,
unless the overriding function is defined as deleted.
\begin{example}
\begin{codeblock}
struct B {
virtual void f() noexcept;
virtual void g();
virtual void h() noexcept = delete;
};

struct D: B {
void f(); // error
void g() noexcept; // OK
void h() = delete; // OK
};
\end{codeblock}

The declaration of
\tcode{D::f}
is ill-formed because it
has a potentially-throwing exception specification,
whereas
\tcode{B::f}
has a non-throwing exception specification.
\end{example}

\pnum
An expression $E$ is
\defnx{potentially-throwing}{potentially-throwing!expression} if
\begin{itemize}
\item
$E$ is a function call\iref{expr.call}
whose \grammarterm{postfix-expression}
has a function type,
or a pointer-to-function type,
with a potentially-throwing exception specification,
or
\item
$E$ implicitly invokes a function
(such as an overloaded operator,
an allocation function in a \grammarterm{new-expression},
a constructor for a function argument,
or a destructor if $E$ is a full-expression\iref{intro.execution})
that has a potentially-throwing exception specification,
or
\item
$E$ is a \grammarterm{throw-expression}\iref{expr.throw},
or
\item
$E$ is a \keyword{dynamic_cast} expression that casts to a reference type and
requires a runtime check\iref{expr.dynamic.cast},
or
\item
$E$ is a \keyword{typeid} expression applied to a
(possibly parenthesized) built-in unary \tcode{*} operator
applied to a pointer to a
polymorphic class type\iref{expr.typeid},
or
\item
any of the immediate subexpressions\iref{intro.execution}
of $E$ is potentially-throwing.
\end{itemize}

\pnum
An implicitly-declared constructor for a class \tcode{X},
or a constructor without a \grammarterm{noexcept-specifier}
that is defaulted on its first declaration,
has a potentially-throwing exception specification
if and only if
any of the following constructs is potentially-throwing:
\begin{itemize}
\item
the invocation of a constructor selected by overload resolution
in the implicit definition of the constructor
for class \tcode{X}
to initialize a potentially constructed subobject, or
\item
a subexpression of such an initialization,
such as a default argument expression, or,
\item
for a default constructor, a default member initializer.
\end{itemize}
\begin{note}
Even though destructors for fully-constructed subobjects
are invoked when an exception is thrown
during the execution of a constructor\iref{except.ctor},
their exception specifications do not contribute
to the exception specification of the constructor,
because an exception thrown from such a destructor
would call the function \tcode{std::terminate}
rather than escape the constructor\iref{except.throw,except.terminate}.
\end{note}

\pnum
The exception specification for an implicitly-declared destructor,
or a destructor without a \grammarterm{noexcept-specifier},
is potentially-throwing if and only if
any of the destructors
for any of its potentially constructed subobjects
has a potentially-throwing exception specification or
the destructor is virtual and the destructor of any virtual base class
has a potentially-throwing exception specification.

\pnum
The exception specification for an implicitly-declared assignment operator,
or an assignment-operator without a \grammarterm{noexcept-specifier}
that is defaulted on its first declaration,
is potentially-throwing if and only if
the invocation of any assignment operator
in the implicit definition is potentially-throwing.

\pnum
A deallocation function\iref{basic.stc.dynamic.deallocation}
with no explicit \grammarterm{noexcept-specifier}
has a non-throwing exception specification.

\pnum
The exception specification for a comparison operator function\iref{over.binary}
without a \grammarterm{noexcept-specifier}
that is defaulted on its first declaration
is potentially-throwing if and only if
any expression
in the implicit definition is potentially-throwing.

\pnum
\begin{example}
\begin{codeblock}
struct A {
A(int = (A(5), 0)) noexcept;
A(const A&) noexcept;
A(A&&) noexcept;
~A();
};
struct B {
B() noexcept;
B(const B&) = default; // implicit exception specification is \tcode{\keyword{noexcept}(\keyword{true})}
B(B&&, int = (throw 42, 0)) noexcept;
~B() noexcept(false);
};
int n = 7;
struct D : public A, public B {
int * p = new int[n];
// \tcode{D::D()} potentially-throwing, as the \keyword{new} operator may throw \tcode{bad_alloc} or \tcode{bad_array_new_length}
// \tcode{D::D(const D\&)} non-throwing
// \tcode{D::D(D\&\&)} potentially-throwing, as the default argument for \tcode{B}'s constructor may throw
// \tcode{D::\~D()} potentially-throwing
};
\end{codeblock}
Furthermore, if
\tcode{A::\~{}A()}
were virtual,
the program would be ill-formed since a function that overrides a virtual
function from a base class
shall not have a potentially-throwing exception specification
if the base class function has a non-throwing exception specification.
\end{example}

\pnum
An exception specification is considered to be \defnx{needed}{needed!exception specification} when:
\begin{itemize}
\item in an expression, the function is selected by
overload resolution\iref{over.match,over.over};

\item the function is odr-used\iref{term.odr.use} or, if it appears in an
unevaluated operand, would be odr-used if the expression were
potentially-evaluated;

\item the exception specification is compared to that of another
declaration (e.g., an explicit specialization or an overriding virtual
function);

\item the function is defined; or

\item the exception specification is needed for a defaulted
function that calls the function.
\begin{note}
A defaulted declaration does not require the
exception specification of a base member function to be evaluated
until the implicit exception specification of the derived
function is needed, but an explicit \grammarterm{noexcept-specifier} needs
the implicit exception specification to compare against.
\end{note}
\end{itemize}
The exception specification of a defaulted
function is evaluated as described above only when needed; similarly, the
\grammarterm{noexcept-specifier} of a specialization of a function
template or member function of a class template is instantiated only when
needed.
%
\indextext{exception specification|)}

\rSec1[dcl.init]{Initializers}%

\rSec2[dcl.init.general]{General}%
Expand Down
Loading

0 comments on commit 886de55

Please sign in to comment.