Skip to content

[lib] Extend exception propagation to cover the initialization LWG3640 #4863

Open
@JohelEGP

Description

@JohelEGP

Problem and solution

The wording for the specification of facilities that propagate exceptions inadvertently exclude some cases of thrown exceptions. The ones I've identified have the form "any exception thrown by the {(selected) constructor,construction} of {T,some object}". This formulation excludes exceptions thrown by the initialization of the constructor's parameters ([expr.call] p7). These are not thrown by the constructor nor during the construction of the object (after its construction has begun ([class.cdtor] p1)).

Implying that the initialization of the constructor's parameter are part of the effects for which exceptions are propagated can be done with these changes:

  • "Any exception thrown by calling the (selected) constructor of T.
  • "Any exception thrown by the constructioninitialization of {T,some object}".

The former is already present in the standard, as exemplified below. As for the latter (emphasis mine),

The process of initialization described in [dcl.init] applies to all initializations regardless of syntactic context, including the initialization of a function parameter ([expr.call]), the initialization of a return value ([stmt.return]), or when an initializer follows a declarator.
-- https://eel.is/c++draft/dcl.init p1

Examples

Example, good

Constructors and member functions of pair do not throw exceptions unless one of the element-wise operations specified to be called for that operation throws an exception. -- https://eel.is/c++draft/pairs.pair#1

Talking in terms of operations includes initialization.

Example, bad

For each tuple constructor, an exception is thrown only if the construction of one of the types in Types throws an exception. -- https://eel.is/c++draft/tuple.cnstr#2

We can make a tuple constructor throw an exception other than by constructing one of the specified types: https://godbolt.org/z/enGThMq4P.

#include <tuple>
struct X { X(int) { } };
struct Y { operator int() { throw 0; } };
int main() { std::tuple<X>(Y{}); }

As described at the top, this throws before beginning the construction of an X subobject.

Do note that the constructor above is described to initialize:

Effects: Initializes the elements in the tuple with the corresponding value in std​::​forward(u). -- https://eel.is/c++draft/tuple.cnstr#13

One could argue that [tuple.cnstr] doesn't say "if and only if". That just leaves in the air what happens in this case.

Example, good

Throws: Any exception thrown by the initialization of the selected alternative Tj. -- https://eel.is/c++draft/variant.ctor#18
Throws: Any exception thrown by calling the selected constructor of T. -- https://eel.is/c++draft/variant.ctor#23

Metadata

Metadata

Assignees

Labels

bigAn issue causing a large set of changes, scattered across most of the text.lwgIssue must be reviewed by LWG.not-editorialIssue is not deemed editorial; the editorial issue is kept open for tracking.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions