1. Overview
I propose to mark the function
.
Currently, this can be done because there can never be an active exception during constant evaluation.
would always return zero during constant evaluation.
Similarly,
would always return a null pointer.
[P2996R1] recommends exceptions as an error handling mechanism for reflections,
during constant evaluation.
If exceptions were throwable in constant expresssions, marking these functions
would simply be part of exception support. However, [P2996R1] is not a prerequisite to this proposal.
2. Motivation
The motivation is the same as allowing
-
blocks in
functions,
a feature added to C++20 thanks to [P1002R1].
Allowing the use of
in constant expressions makes
metaprogramming easier and eliminates special
cases.
A common use case for
is in the implementation of RAII types which
execute a function upon destruction, but only if an exception was (not) thrown in the current
scope.
This is utilized by
and
; see [N4806].
For example, in the implementation of a
stack, one may write:
constexpr value_type pop () { // Only decrease the size of the stack if no exception was thrown during // copy/move construction of the returned object. // This ensures a strong exception guarantee. std :: scope_success _ {[ this ] { m_size -- ; }}; return std :: move ( top ()); }
It is reasonable to mark such code
, and ideally
would not be an
obstacle to this.
Furthermore, it makes sense to mark
' sister function,
.
This is done purely for the purpose of consistency.
I am not aware of any concrete example of
’s
lack of
being an obstacle.
3. Possible implementation
3.1. constexpr uncaught_exceptions
constexpr int uncaught_exceptions () noexcept { if consteval { return 0 ; } else { return __uncaught_exceptions_impl (); } }
It is obviously possible for the user to wrap
like this themselves (e.g. [ScopeLite]), but this is an unnecessary burden.
3.2. constexpr current_exception
constexpr exception_ptr current_exception () noexcept { if consteval { return exception_ptr ( nullptr ); } else { return __current_exception_impl (); } }
3.3. constexpr
exception_ptr
would also need to be made a literal type.
All major three standard libraries implement
as a wrapper class for
,
which makes this easily possible.
-
See MSVC STL, exception.
Simply mark all special member functions
and if necessary, guard their implementation
with an
block.
It is impossible to create an
that is not a null pointer during constant
evaluations.
3.4. Non-trivial implementations
[P2996R1] suggests allowing
in constant expressions.
This would mean that
,
,
and
would no longer have such trivial implementations,
and further functions such as
may be marked
.
The bare minimum compiler support needed for this is:
-
The compiler must track all active exceptions "magically", so that
returns the correct amount, andstd :: active_exceptions ()
returns the current exception. This needs compiler support because such mutable global state normally doesn’t exist in constant expressions.std :: current_exception () -
behaves like a type-erased, reference-counted smart pointer. [P2738R1] has been accepted into C++26, addingstd :: exception_ptr
cast fromconstexpr
. This makes the implementation of such type-erasure invoid *
feasible.constexpr std :: exception_ptr
3.5. Impact on ABI
Multiple functions, including member functions of
would become
inline functions if marked
.
To remain ABI-compatible with existing software, it is necessary to emit these inline function
into the runtime library.
libstdc++ already conditionally does this by marking member functions of
.
Therefore:
-
libstdc++ is not affected.
-
MSVC STL already defines all member function of
as inline functions, and is also not affected.exception_ptr -
libc++ would need to apply similar compatibility measures as libstdc++.
4. Proposed wording
The proposed changes are relative to the working draft of the standard as of [N4917].
Update subclause 17.3.2 [version.syn], paragraph 2 as follows:
#define __cpp_lib_constexpr_current_exception 202401L // freestanding, also in <exception> [...] #define __cpp_lib_uncaught_exceptions 201411L 202401L // freestanding, also in <exception>
Update subclause 17.9.6 [uncaught.exceptions] as follows:
constexpr int uncaught_exceptions () noexcept ;
Update subclause 17.9.7 [propagation], paragraph 2 as follows:
is a literal type([basic.types.general]) which meets the requirements of Cpp17NullablePointer (Table 36). All expressions which must be valid for a Cpp17NullablePointer are constant expressions for a null value of type
exception_ptr .
exception_ptr
Note: This wording is slightly work-in-progress.
Update subclause 17.9.7 [propagation],
as follows:
constexpr exception_ptr current_exception () noexcept ;
5. Acknowledgements
The original idea for this paper and a portion of its content have been adopted from a proposal draft by Morwenn.