From 91648ff32fa0c0e811e50d88a11f6dda52ebe2b4 Mon Sep 17 00:00:00 2001 From: Eisenwave Date: Sun, 29 Mar 2026 22:58:15 +0200 Subject: [PATCH] P3726R2 Adjustments to Union Lifetime Rules Fixes NB DE 087, US 48-086 (C++26 CD). Editorial notes: * Missing "freestanding" comment added to the feature test macro line. * Removed "type" after "aggregate" (just "aggregate" is the right term). --- source/basic.tex | 52 +++++++++++++++++++++++++++++++++++++++-- source/classes.tex | 13 +---------- source/memory.tex | 27 +++++++++++++++++++++ source/preprocessor.tex | 2 +- source/support.tex | 1 + source/templates.tex | 4 +++- 6 files changed, 83 insertions(+), 16 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index bd79b3cfb9..f44bf1414a 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -3694,6 +3694,16 @@ the address of an unspecified byte of storage occupied by the complete object of that subobject. +\pnum +A \defnadj{union elemental}{subobject} +\indextext{union!elemental subobject|see{subobject, union elemental}} +is a direct member of a union or +an element of an array that is a union elemental subobject. +An \defnx{inactive union elemental subobject}{subobject!union elemental, inactive} +\indextext{inactive union elemental subobject|see{subobject, union elemental, inactive}} +\indextext{union!inactive elemental subobject|see{subobject, union elemental, inactive}} +is a union elemental subobject that is not within its lifetime. + \pnum The \defnx{constituent values}{constituent value} of an object $o$ are \begin{itemize} @@ -3701,7 +3711,7 @@ if $o$ has scalar type, the value of $o$; \item otherwise, the constituent values of any direct subobjects of $o$ -other than inactive union members. +other than inactive union elemental subobjects. \end{itemize} The \defnx{constituent references}{constituent reference} of an object $o$ are \begin{itemize} @@ -3709,8 +3719,46 @@ any direct members of $o$ that have reference type, and \item the constituent references of any direct subobjects of $o$ -other than inactive union members. +other than inactive union elemental subobjects. \end{itemize} +\begin{example} +\begin{codeblock} +struct A { + struct X { + int i; + int j; + }; + + struct Y { + X x1; + X x2; + }; + + union { + int i; + int arr[4]; + Y y; + }; +}; + +constexpr A v1; // OK, no constituent values +constexpr A v2{.i=1}; // OK, the constituent values are \tcode{\{v2.i\}} +constexpr A v3 = []{ + A a; + std::start_lifetime(a.arr); // OK, arr is now the active element of the union + new (&a.arr[1]) int(1); + a.arr[2] = 2; + return a; +}(); // OK, the constituent values are \tcode{\{v3.arr[1], v3.arr[2]\}} +constexpr A v4 = []{ + A a; + a.y.x1.i = 1; + a.y.x2.j = 2; + return a; +}(); // error: the constituent values include \tcode{v4.y.x1.j} and \tcode{v4.y.x2.i} + // which have erroneous value +\end{codeblock} +\end{example} \pnum Some operations are described as diff --git a/source/classes.tex b/source/classes.tex index 5a0c0ee597..08134ae8b9 100644 --- a/source/classes.tex +++ b/source/classes.tex @@ -1337,18 +1337,7 @@ \defnx{non-trivial}{constructor!default!non-trivial}. \pnum -If a default constructor of a union-like class \tcode{X} is trivial, -then for each union \tcode{U} -that is either \tcode{X} or an anonymous union member of \tcode{X}, -if the first variant member, if any, of \tcode{U} -has implicit-lifetime type\iref{basic.types.general}, -the default constructor of \tcode{X} begins the lifetime of that member -if it is not the active member of its union. -\begin{note} -It is already the active member if \tcode{U} was value-initialized. -\end{note} -Otherwise, -an implicitly-defined\iref{dcl.fct.def.default} default constructor performs the set of +An implicitly-defined\iref{dcl.fct.def.default} default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no \grammarterm{ctor-initializer}\iref{class.base.init} and an empty diff --git a/source/memory.tex b/source/memory.tex index a7f16411e8..7bd8bde552 100644 --- a/source/memory.tex +++ b/source/memory.tex @@ -88,6 +88,8 @@ bool is_sufficiently_aligned(T* ptr); // freestanding // \ref{obj.lifetime}, explicit lifetime management + template + constexpr void start_lifetime(T& r) noexcept; // freestanding template T* start_lifetime_as(void* p) noexcept; // freestanding template @@ -1036,6 +1038,31 @@ \rSec2[obj.lifetime]{Explicit lifetime management} +\indexlibraryglobal{start_lifetime}% +\begin{itemdecl} +template + constexpr void start_lifetime(T& r) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T} is a complete type and +an implicit-lifetime\iref{basic.types} aggregate\iref{dcl.init.aggr}. + +\pnum +\effects +If the object referenced by \tcode{r} +is already within its lifetime\iref{basic.life}, +no effects. +Otherwise, begins the lifetime of the object referenced by \tcode{r}. +\begin{note} +No initialization is performed and no subobject has its lifetime started. +If \tcode{r} denotes a member of a union $U$, +it becomes the active member of $U$\iref{class.union}. +\end{note} +\end{itemdescr} + \indexlibraryglobal{start_lifetime_as}% \begin{itemdecl} template diff --git a/source/preprocessor.tex b/source/preprocessor.tex index f3037b19ad..c2284a582c 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -2451,7 +2451,7 @@ \defnxname{cpp_template_parameters} & \tcode{202502L} \\ \rowsep \defnxname{cpp_template_template_args} & \tcode{201611L} \\ \rowsep \defnxname{cpp_threadsafe_static_init} & \tcode{200806L} \\ \rowsep -\defnxname{cpp_trivial_union} & \tcode{202502L} \\ \rowsep +\defnxname{cpp_trivial_union} & \tcode{202603L} \\ \rowsep \defnxname{cpp_unicode_characters} & \tcode{200704L} \\ \rowsep \defnxname{cpp_unicode_literals} & \tcode{200710L} \\ \rowsep \defnxname{cpp_user_defined_literals} & \tcode{200809L} \\ \rowsep diff --git a/source/support.tex b/source/support.tex index 203a1bf45f..fdc8a05421 100644 --- a/source/support.tex +++ b/source/support.tex @@ -844,6 +844,7 @@ #define @\defnlibxname{cpp_lib_ssize}@ 201902L // freestanding, also in \libheader{iterator} #define @\defnlibxname{cpp_lib_sstream_from_string_view}@ 202306L // also in \libheader{sstream} #define @\defnlibxname{cpp_lib_stacktrace}@ 202011L // also in \libheader{stacktrace} +#define @\defnlibxname{cpp_lib_start_lifetime}@ 202603L // freestanding, also in \libheader{memory} #define @\defnlibxname{cpp_lib_start_lifetime_as}@ 202207L // freestanding, also in \libheader{memory} #define @\defnlibxname{cpp_lib_starts_ends_with}@ 201711L // also in \libheader{string}, \libheader{string_view} #define @\defnlibxname{cpp_lib_stdatomic_h}@ 202011L // also in \libheader{stdatomic.h} diff --git a/source/templates.tex b/source/templates.tex index 87c4bb0434..9effb38ee5 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -2574,7 +2574,9 @@ they are of reference type and they refer to the same object or function, or \item -they are of array type and their corresponding elements are template-argument-equivalent, +they are of array type and their corresponding elements are either +both within their lifetimes and template-argument-equivalent or +both not within their lifetimes, \begin{footnote} An array as a \grammarterm{template-parameter} decays to a pointer. \end{footnote}