From d9a33de918ce2c87a577be23b4e73633a3291e0e Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 14 Jun 2026 11:55:24 +0200 Subject: [PATCH 1/2] P2287R6 Designated-initializers for Base Classes --- source/compatibility.tex | 33 ++++++++ source/declarations.tex | 161 +++++++++++++++++++++++++++++++++------ source/preprocessor.tex | 2 +- 3 files changed, 172 insertions(+), 24 deletions(-) diff --git a/source/compatibility.tex b/source/compatibility.tex index 7f97796edc..3d78f3f569 100644 --- a/source/compatibility.tex +++ b/source/compatibility.tex @@ -1,6 +1,39 @@ %!TEX root = std.tex \infannex{diff}{Compatibility} +\rSec1[diff.cpp26]{\Cpp{} and ISO \CppXXVI{}} + +\rSec2[diff.cpp26.general]{General} + +\pnum +\indextext{summary!compatibility with ISO \CppXXVI{}}% +Subclause \ref{diff.cpp26} lists the differences between \Cpp{} and +ISO \CppXXVI{}, +by the chapters of this document. + +\rSec2[diff.cpp26.dcl]{\ref{dcl}: declarations} + +\diffref{dcl.init} +\change +Support for designated initialization of base classes of aggregates. +\rationale +New functionality. +\effect +Some valid \CppXXVI{} code may fail to compile. +\begin{example} +\begin{codeblock} +struct A { int a; }; +struct B : A { int b; }; + +void f(A); // \#1 +void f(B); // \#2 + +void g() { + f({.a=1}); // ambiguous between \#1 and \#2; previously called \#1 +} +\end{codeblock} +\end{example} + \rSec1[diff.cpp23]{\Cpp{} and ISO \CppXXIII{}} \rSec2[diff.cpp23.general]{General} diff --git a/source/declarations.tex b/source/declarations.tex index 627360a226..a198a2ff8a 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -4959,9 +4959,15 @@ \end{bnf} \begin{bnf} -\nontermdef{designated-initializer-list}\br +\nontermdef{designated-only-initializer-list}\br designated-initializer-clause\br - designated-initializer-list \terminal{,} designated-initializer-clause + designated-only-initializer-list \terminal{,} designated-initializer-clause +\end{bnf} + +\begin{bnf} +\nontermdef{designated-initializer-list}\br + designated-only-initializer-list\br + initializer-list \terminal{,} designated-only-initializer-list \end{bnf} \begin{bnf} @@ -5525,6 +5531,38 @@ that are not members of an anonymous union, in declaration order. \end{itemize} +\pnum +The \defnadj{associated}{element} of a member \tcode{M} +of an aggregate \tcode{T} is: +\begin{itemize} +\item \tcode{M} if \tcode{M} is an element of \tcode{T}; +\item otherwise, the element of \tcode{T} that contains \tcode{M}. +\end{itemize} +\begin{example} +\begin{codeblock} +struct A { + int a1; + union { + int a2; + char a3; + }; +}; + +struct B : A { + int b1; + union { + double b2; + }; +}; +\end{codeblock} +The associated element of each of +the members \tcode{A::a1}, \tcode{A::a2}, and \tcode{A::a3} +of \tcode{B} is \tcode{A}. +The associated element of the member \tcode{B::b1} of \tcode{B} is itself. +The associated element of the member \tcode{B::b2} of \tcode{B} is +the anonymous union containing it. +\end{example} + \pnum When an aggregate is initialized by an initializer list as specified in~\ref{dcl.init.list}, @@ -5536,32 +5574,74 @@ \item If the initializer list is a brace-enclosed \grammarterm{designated-initializer-list}, -the aggregate shall be of class type, -the \grammarterm{identifier} in each \grammarterm{designator} -shall name a direct non-static data member of the class, and -the explicitly initialized elements of the aggregate -are the elements that are, or contain, those members. +the aggregate shall be of class type \tcode{C}. +For each \tcode{designator} +within the \grammarterm{designated-only-initializer-list} +of the \grammarterm{designated-initializer-list}, +the lookup set for the \grammarterm{identifier} +in \tcode{C}\iref{class.member.lookup} shall comprise +\begin{itemize} +\item +a declaration set consisting of a single non-static data member and +\item +a subobject set containing only one subobject, +whose type shall be an aggregate base of \tcode{C}. +An aggregate \tcode{B} is an \defnadj{aggregate}{base} of a class \tcode{D} +if it is \tcode{D} or a direct base class of an aggregate base of \tcode{D}. +\end{itemize} + +That non-static data member is \defnx{designated}{designate!aggregate element} +by the \grammarterm{identifier}. +Each \grammarterm{initializer-clause} of the \grammarterm{initializer-list}, +if any, +shall appertain (see below) to a (direct) base class subobject of \tcode{C}. +The explicitly initialized elements of the aggregate include +the associated elements of each member \tcode{M} of \tcode{C} +for which \tcode{M} is designated by an \grammarterm{identifier} +in a \grammarterm{designated-initializer-clause}. + \item -If the initializer list is a brace-enclosed \grammarterm{initializer-list}, +If the \grammarterm{braced-init-list} has an \grammarterm{initializer-list} +(possibly within a \grammarterm{designated-initializer-list}) the explicitly initialized elements of the aggregate -are those for which an element of the initializer list +include those for which an element of the initializer list appertains to the aggregate element or to a subobject thereof (see below). \item -Otherwise, the initializer list must be \tcode{\{\}}, -and there are no explicitly initialized elements. +No other elements of the aggregate are explicitly initialized. +\begin{note} +The initializer \tcode{\{\}} does not explicitly initialize +any elements of the aggregate. +\end{note} \end{itemize} +If any element of the aggregate is explicitly initialized per +both an \grammarterm{initializer-list} and +a \grammarterm{designated-only-initializer-list}, +the program is ill-formed. +\begin{example} +\begin{codeblock} +struct A { int a1, a2; }; +struct B : A { int b; }; +struct C : A { int a1; }; + +A v0 = A{1, .a2=2}; // error: \grammarterm{initializer-clause} appertains to non-static data member +B v1 = B{.a1=1, .b=2}; // the explicitly initialized elements are [\tcode{A}, \tcode{B::b}] +B v2 = B{.a1=1, .a2=2, .b=3}; // the explicitly initialized elements are [\tcode{A}, \tcode{B::b}] +B v3 = B{A{1, 2}, .b=3}; // the explicitly initialized elements are [\tcode{A}, \tcode{B::b}] +B v4 = B{A{}, .a2=1, .b=3}; // error: \tcode{A} initialized two different ways +C v5 = C{.a1=4}; // the explicitly initialized elements are [\tcode{C::a1}] +\end{codeblock} +\end{example} \pnum For each explicitly initialized element: \begin{itemize} \item If the element is an anonymous union member and -the initializer list is -a brace-enclosed \grammarterm{designated-initializer-list}, +is explicitly initialized per a \grammarterm{designated-only-initializer-list}, the element is initialized by the \grammarterm{braced-init-list} \tcode{\{ }\placeholder{D}\tcode{ \}}, where \placeholder{D} is the \grammarterm{designated-initializer-clause} -naming a member of the anonymous union member. +whose associated element is the anonymous union member. There shall be only one such \grammarterm{designated-initializer-clause}. \begin{example} \begin{codeblock} @@ -5576,8 +5656,11 @@ initializes \tcode{c.a} with 1 and \tcode{c.x} with 3. \end{example} \item -Otherwise, if the initializer list is -a brace-enclosed \grammarterm{designated-initializer-list}, +Otherwise, if the element is explicitly initialized +per a \grammarterm{designated-only-initializer-list}, then +\begin{itemize} +\item +if the element is a (direct) non-static data member, then the element is initialized with the \grammarterm{brace-or-equal-initializer} of the corresponding \grammarterm{designated-initializer-clause}. If that initializer is of the form @@ -5590,8 +5673,29 @@ whether copy-initialization or direct-initialization is performed. \end{note} \item +otherwise, the element is a base class subobject $B$, and +is copy-initialized from +a brace-enclosed \grammarterm{designated-initializer-list} +consisting of all of the \grammarterm{designated-initializer-clause}s +whose associated element is $B$, in order. +\end{itemize} +\begin{example} +\begin{codeblock} +struct A { int a; }; +struct B : A { int b; }; +struct C : B { int c; }; + +// the \tcode{A} element is initialized from \tcode{\{.a=1\}} +B x = B{.a=1}; + +// the \tcode{B} element is initialized from \tcode{\{.a=2, .b=3\}} +// which leads to its \tcode{A} element being initialized from \tcode{\{.a=2\}} +C y = C{.a=2, .b=3, .c=4}; +\end{codeblock} +\end{example} +\item Otherwise, -the initializer list is a brace-enclosed \grammarterm{initializer-list}. +element is explicitly initialized per an \grammarterm{initializer-list}. If an \grammarterm{initializer-clause} appertains to the aggregate element, then the aggregate element is copy-initialized from the \grammarterm{initializer-clause}. Otherwise, @@ -5857,8 +5961,7 @@ \end{example} \pnum -Each \grammarterm{initializer-clause} in -a brace-enclosed \grammarterm{initializer-list} +Each \grammarterm{initializer-clause} in a \grammarterm{braced-init-list} is said to \defn{appertain} to an element of the aggregate being initialized or to an element of one of its subaggregates. @@ -6464,18 +6567,30 @@ contains a \grammarterm{designated-initializer-list} and \tcode{T} is not a reference type, \tcode{T} shall be an aggregate class. -The ordered \grammarterm{identifier}{s} +The associated elements of the non-static data members of \tcode{T} +designated by the ordered \grammarterm{identifier}{s} in the \grammarterm{designator}{s} of the \grammarterm{designated-initializer-list} -shall form a subsequence -of the ordered \grammarterm{identifier}{s} -in the direct non-static data members of \tcode{T}. +shall be in non-decreasing element order. Aggregate initialization is performed\iref{dcl.init.aggr}. \begin{example} \begin{codeblock} struct A { int x; int y; int z; }; A a{.y = 2, .x = 1}; // error: designator order does not match declaration order A b{.x = 1, .z = 2}; // OK, \tcode{b.y} initialized to \tcode{0} + +struct B : A { int q; }; +B e{.x = 1, .q = 3}; // OK, \tcode{e.y} and \tcode{e.z} initialized to \tcode{0} +B f{.q = 3, .x = 1}; // error: designator order does not match declaration order + +struct C { int p; int x; }; +struct D : A, C { }; +D g{.y=1, .p=2}; // OK +D h{.x=2}; // error: ambiguous lookup for \tcode{x} + +struct NonAggr { int na; NonAggr(int); }; +struct E : NonAggr { int e; }; +E i{.na=1, .e=2}; // error: the lookup set for \tcode{na} finds \tcode{NonAggr}, which is not an aggregate base of \tcode{E} \end{codeblock} \end{example} diff --git a/source/preprocessor.tex b/source/preprocessor.tex index 0bfb7f194f..ac08563790 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -2398,7 +2398,7 @@ \defnxname{cpp_deduction_guides} & \tcode{202207L} \\ \rowsep \defnxname{cpp_delegating_constructors} & \tcode{200604L} \\ \rowsep \defnxname{cpp_deleted_function} & \tcode{202403L} \\ \rowsep -\defnxname{cpp_designated_initializers} & \tcode{201707L} \\ \rowsep +\defnxname{cpp_designated_initializers} & \tcode{202606L} \\ \rowsep \defnxname{cpp_enumerator_attributes} & \tcode{201411L} \\ \rowsep \defnxname{cpp_expansion_statements} & \tcode{202506L} \\ \rowsep \defnxname{cpp_explicit_this_parameter} & \tcode{202110L} \\ \rowsep From e9110c635a7fa7afd9e49661b165ee4ba579384a Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Sun, 14 Jun 2026 23:03:51 +0200 Subject: [PATCH 2/2] fixup: markup --- source/declarations.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/declarations.tex b/source/declarations.tex index a198a2ff8a..db1106573b 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -5575,7 +5575,7 @@ If the initializer list is a brace-enclosed \grammarterm{designated-initializer-list}, the aggregate shall be of class type \tcode{C}. -For each \tcode{designator} +For each \grammarterm{designator} within the \grammarterm{designated-only-initializer-list} of the \grammarterm{designated-initializer-list}, the lookup set for the \grammarterm{identifier}