From e873d2fc79de4a7440d170cb16d3e073fa6b6758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Sun, 22 Jun 2025 22:39:03 +0100 Subject: [PATCH 1/2] P1306R5 Expansion Statements Editorial notes: * [stmt.pre] The sentence moved up from [stmt.iter.gen] is moved _after_ the existing sentence instead of before. * [stmt.expand] Missing full stop included in Note 1. --- source/basic.tex | 22 ++- source/declarations.tex | 3 +- source/preprocessor.tex | 1 + source/statements.tex | 302 +++++++++++++++++++++++++++++++--------- source/templates.tex | 35 ++++- 5 files changed, 288 insertions(+), 75 deletions(-) diff --git a/source/basic.tex b/source/basic.tex index 8178129463..2447e6550c 100644 --- a/source/basic.tex +++ b/source/basic.tex @@ -1281,6 +1281,9 @@ The locus of a \grammarterm{for-range-declaration} of a range-based \keyword{for} statement\iref{stmt.ranged} is immediately after the \grammarterm{for-range-initializer}. +The locus of a \grammarterm{for-range-declaration} +of an expansion statement\iref{stmt.expand} +is immediately after the \grammarterm{expansion-initializer}. \pnum The locus of a \grammarterm{template-parameter} is immediately after it. @@ -1341,7 +1344,7 @@ Each \begin{itemize} \item -selection or iteration statement\iref{stmt.select,stmt.iter}, +selection, iteration, or expansion statement\iref{stmt.select,stmt.iter,stmt.expand}, \item substatement of such a statement, \item @@ -4591,7 +4594,7 @@ \pnum \indextext{initializer!temporary and declarator}% \indextext{temporary!order of destruction of}% -There are five contexts in which temporaries are destroyed at a different +There are several contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default constructor is called to initialize an element of an array with no corresponding initializer\iref{dcl.init}. @@ -4711,14 +4714,25 @@ \pnum The fourth context is when a temporary object -is created in the \grammarterm{for-range-initializer} of a range-based \keyword{for} statement. +is created in the \grammarterm{for-range-initializer} of +either a range-based \keyword{for} statement +or an enumerating expansion statement\iref{stmt.expand}. If such a temporary object would otherwise be destroyed at the end of the \grammarterm{for-range-initializer} full-expression, the object persists for the lifetime of the reference initialized by the \grammarterm{for-range-initializer}. \pnum -The fifth context is when a temporary object +The fifth context is when a temporary object is created +in the \grammarterm{expansion-initializer} +of an iterating or destructuring expansion statement. +If such a temporary object would otherwise be destroyed +at the end of that \grammarterm{expansion-initializer}, +the object persists for the lifetime of the reference +initialized by the \grammarterm{expansion-initializer}, if any. + +\pnum +The sixth context is when a temporary object is created in a structured binding declaration\iref{dcl.struct.bind}. Any temporary objects introduced by the \grammarterm{initializer}{s} for the variables diff --git a/source/declarations.tex b/source/declarations.tex index b941b09c2e..0138279403 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -7550,7 +7550,8 @@ the program is ill-formed. \pnum -If \tcode{E} is an array type with element type \tcode{T}, +\tcode{E} shall not be an array type of unknown bound. +If \tcode{E} is any other array type with element type \tcode{T}, the structured binding size of \tcode{E} is equal to the number of elements of \tcode{E}. Each $\textrm{SB}_i$ is the name of an diff --git a/source/preprocessor.tex b/source/preprocessor.tex index d5f6e0ba7d..e315e0f7f5 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -2311,6 +2311,7 @@ \defnxname{cpp_deleted_function} & \tcode{202403L} \\ \rowsep \defnxname{cpp_designated_initializers} & \tcode{201707L} \\ \rowsep \defnxname{cpp_enumerator_attributes} & \tcode{201411L} \\ \rowsep +\defnxname{cpp_expansion_statements} & \tcode{202506L} \\ \rowsep \defnxname{cpp_explicit_this_parameter} & \tcode{202110L} \\ \rowsep \defnxname{cpp_fold_expressions} & \tcode{201603L} \\ \rowsep \defnxname{cpp_generic_lambdas} & \tcode{201707L} \\ \rowsep diff --git a/source/statements.tex b/source/statements.tex index ce3a65e00e..d98beadd05 100644 --- a/source/statements.tex +++ b/source/statements.tex @@ -18,6 +18,7 @@ \opt{attribute-specifier-seq} compound-statement\br \opt{attribute-specifier-seq} selection-statement\br \opt{attribute-specifier-seq} iteration-statement\br + \opt{attribute-specifier-seq} expansion-statement\br \opt{attribute-specifier-seq} jump-statement\br \opt{attribute-specifier-seq} assertion-statement\br declaration-statement\br @@ -38,7 +39,20 @@ structured-binding-declaration initializer \end{bnf} +\begin{bnf} +\nontermdef{for-range-declaration}\br + \opt{attribute-specifier-seq} decl-specifier-seq declarator\br + structured-binding-declaration +\end{bnf} + +\begin{bnf} +\nontermdef{for-range-initializer}\br + expr-or-braced-init-list +\end{bnf} + The optional \grammarterm{attribute-specifier-seq} appertains to the respective statement. +See~\ref{dcl.meaning} for the optional \grammarterm{attribute-specifier-seq} in a +\grammarterm{for-range-declaration}. \pnum A \defn{substatement} of a \grammarterm{statement} is one of the following: @@ -48,9 +62,12 @@ \item for a \grammarterm{compound-statement}, any \grammarterm{statement} of its \grammarterm{statement-seq}, \item - for a \grammarterm{selection-statement}, any of its \grammarterm{statement}{s} or \grammarterm{compound-statement}{s} (but not its \grammarterm{init-statement}), or + for a \grammarterm{selection-statement}, any of its \grammarterm{statement}{s} or \grammarterm{compound-statement}{s} (but not its \grammarterm{init-statement}), +\item + for an \grammarterm{iteration-statement}, its \grammarterm{statement} (but not an \grammarterm{init-statement}), or \item - for an \grammarterm{iteration-statement}, its \grammarterm{statement} (but not an \grammarterm{init-statement}). + for an \grammarterm{expansion-statement}, its \grammarterm{compound-statement} + (but not an \grammarterm{init-statement}). \end{itemize} \begin{note} The \grammarterm{compound-statement} of a \grammarterm{lambda-expression} @@ -66,9 +83,10 @@ \item \tcode{S2} is a substatement of \tcode{S1}, \item - \tcode{S1} is a \grammarterm{selection-statement} or - \grammarterm{iteration-statement} and - \tcode{S2} is the \grammarterm{init-statement} of \tcode{S1}, + \tcode{S1} is a \grammarterm{selection-statement}, + \grammarterm{iteration-statement}, or + \grammarterm{expansion-statement}, + and \tcode{S2} is the \grammarterm{init-statement} of \tcode{S1}, \item \tcode{S1} is a \grammarterm{try-block} and \tcode{S2} is its \grammarterm{compound-statement} or @@ -129,12 +147,15 @@ it is interpreted as the latter. \pnum -In the \grammarterm{decl-specifier-seq} of a \grammarterm{condition}, +In the \grammarterm{decl-specifier-seq} of a \grammarterm{condition} +or of a \grammarterm{for-range-declaration}, including that of any \grammarterm{structured-binding-declaration} of the \grammarterm{condition}, each \grammarterm{decl-specifier} shall be either a \grammarterm{type-specifier} or \keyword{constexpr}. +The \grammarterm{decl-specifier-seq} of a \grammarterm{for-range-declaration} +shall not define a class or enumeration. \rSec1[stmt.label]{Label}% \indextext{statement!labeled} @@ -186,6 +207,9 @@ statement\iref{stmt.goto} in \tcode{S}. \end{itemize} +\pnum +An identifier label shall not be enclosed +by an \grammarterm{expansion-statement}\iref{stmt.expand}. \rSec1[stmt.expr]{Expression statement}% \indextext{statement!expression} @@ -519,19 +543,6 @@ \keyword{for} \terminal{(} \opt{init-statement} for-range-declaration \terminal{:} for-range-initializer \terminal{)} statement \end{bnf} -\begin{bnf} -\nontermdef{for-range-declaration}\br - \opt{attribute-specifier-seq} decl-specifier-seq declarator\br - structured-binding-declaration -\end{bnf} - -\begin{bnf} -\nontermdef{for-range-initializer}\br - expr-or-braced-init-list -\end{bnf} - -See~\ref{dcl.meaning} for the optional \grammarterm{attribute-specifier-seq} in a -\grammarterm{for-range-declaration}. \begin{note} An \grammarterm{init-statement} ends with a semicolon. \end{note} @@ -772,13 +783,202 @@ \end{codeblock} \end{example} +\indextext{statement!iteration|)} + +\rSec1[stmt.expand]{Expansion statements} +\indextext{statement!expansion|(} + \pnum -In the \grammarterm{decl-specifier-seq} of a \grammarterm{for-range-declaration}, -each \grammarterm{decl-specifier} shall be either a \grammarterm{type-specifier} -or \keyword{constexpr}. The \grammarterm{decl-specifier-seq} shall not define a -class or enumeration.% +Expansion statements specify repeated instantiations\iref{temp.decls.general} +of their substatement. -\indextext{statement!iteration|)} +\begin{bnf} +\nontermdef{expansion-statement}\br + \keyword{template} \keyword{for} \terminal{(} + \opt{init-statement} for-range-declaration \terminal{:} + expansion-initializer \terminal{)} compound-statement +\end{bnf} + +\begin{bnf} +\nontermdef{expansion-initializer}\br + expression\br + expansion-init-list +\end{bnf} + +\begin{bnf} +\nontermdef{expansion-init-list}\br + \terminal{\{} \opt{expression-list} \terminal{\}} +\end{bnf} + +\pnum +The \grammarterm{compound-statement} of an \grammarterm{expansion-statement} +is a control-flow-limited statement\iref{stmt.label}. + +\pnum +For an expression \tcode{E}, let the expressions +\exposid{begin-expr} and \exposid{end-expr} be determined as specified in~\ref{stmt.ranged}. +An expression is \defn{expansion-iterable} if it does not have array type and either +\begin{itemize} +\item +\exposid{begin-expr} and \exposid{end-expr} are of the form +\tcode{E.begin()} and \tcode{E.end()} or +\item +argument-dependent lookups for \tcode{begin(E)} and for \tcode{end(E)} +each find at least one function or function template. +\end{itemize} + +\pnum +An expansion statement is +\begin{itemize} +\item +an \defnadj{enumerating}{expansion statement} if its \grammarterm{expansion-initializer} +is of the form \grammarterm{expansion-init-list}; +\item +otherwise, an \defnadj{iterating}{expansion statement} if its \grammarterm{expansion-initializer} +is an expansion-iterable expression; +\item +otherwise, a \defnadj{destructuring}{expansion statement}. +\end{itemize} + +\pnum +An expansion statement $S$ is equivalent to a \grammarterm{compound-statement} +containing instantiations of the \grammarterm{for-range-declaration} +(including its implied initialization), +together with the compound-statement of $S$, as follows: +\begin{itemize} +\item +If $S$ is an enumerating expansion statement, $S$ is equivalent to: +\begin{codeblock} +{ + @\grammarterm{init-statement}@ + @$S_{0}$@ + @\vdots@ + @$S_{N-1}$@ +} +\end{codeblock} +where $N$ is the number of elements in the \grammarterm{expression-list}, +$S_{i}$ is +\begin{codeblock} +{ + @\grammarterm{for-range-declaration}@ = @$E_{i}$@; + @\grammarterm{compound-statement}@ +} +\end{codeblock} +and $E_{i}$ is the $i^{\text{th}}$ element of the \grammarterm{expression-list}. + +\item +Otherwise, if $S$ is an iterating expansion statement, $S$ is equivalent to: +\begin{codeblock} +{ + @\grammarterm{init-statement}@ + static constexpr auto&& @\exposidnc{range}@ = @\grammarterm{expansion-initializer}@; + static constexpr auto @\exposidnc{begin}@ = @\exposidnc{begin-expr}@; // see \ref{stmt.ranged} + static constexpr auto @\exposidnc{end}@ = @\exposidnc{end-expr}@; // see \ref{stmt.ranged} + + @$S_{0}$@ + @\vdots@ + @$S_{N-1}$@ +} +\end{codeblock} +where $N$ is the result of evaluating the expression +\begin{codeblock} +[] consteval { + std::ptrdiff_t result = 0; + for (auto i = @\exposid{begin}@; i != @\exposid{end}@; ++i, ++result); + return result; // distance from \exposid{begin} to \exposid{end} +}() +\end{codeblock} +and $S_{i}$ is +\begin{codeblock} +{ + static constexpr auto @\exposid{iter}@ = @\exposid{begin}@ + i; + @\grammarterm{for-range-declaration}@ = *@\exposid{iter}@; + @\grammarterm{compound-statement}@ +} +\end{codeblock} +The variables \exposid{range}, \exposid{begin}, \exposid{end}, and \exposid{iter} +are defined for exposition only. +\begin{note} +The instantiation is ill-formed if \exposid{range} +is not a constant expression\iref{expr.const}. +\end{note} + +\item +Otherwise, $S$ is a destructuring expansion statement and $S$ is equivalent to: +\begin{codeblock} +{ + @\grammarterm{init-statement}@ + @\opt{\keyword{constexpr}}@ auto&& [@$u_{0}$@, @$u_{1}$@, @$\dotsc$@, @$u_{N-1}$@] = @\grammarterm{expansion-initializer}@; + @$S_{0}$@ + @\vdots@ + @$S_{N-1}$@ +} +\end{codeblock} +where $N$ is the structured binding size of the type of the expansion-initializer and $S_{i}$ is +\begin{codeblock} +{ + @\grammarterm{for-range-declaration}@ = @$u_{i}$@; + @\grammarterm{compound-statement}@ +} +\end{codeblock} +The keyword \keyword{constexpr} is present in the declaration +of $u_{0}, u_{1}, \dotsc, u_{N-1}$ if and only if +\keyword{constexpr} is one of the \grammarterm{decl-specifier}s +of the \grammarterm{decl-specifier-seq} +of the \grammarterm{for-range-declaration}. +\end{itemize} + +\pnum +\begin{example} +\begin{codeblock} +consteval int f(auto const&... Containers) { + int result = 0; + template for (auto const& c : {Containers...}) { // OK, enumerating expansion statement + result += c[0]; + } + return result; +} +constexpr int c1[] = {1, 2, 3}; +constexpr int c2[] = {4, 3, 2, 1}; +static_assert(f(c1, c2) == 5); +\end{codeblock} +\end{example} + +\pnum +\begin{example} +\begin{codeblock} +consteval int f() { + constexpr std::array arr {1, 2, 3}; + int result = 0; + template for (constexpr int s : arr) { // OK, iterating expansion statement + result += sizeof(char[s]); + } + return result; +} +static_assert(f() == 6); +\end{codeblock} +\end{example} + +\pnum +\begin{example} +\begin{codeblock} +struct S { + int i; + short s; +}; + +consteval long f(S s) { + long result = 0; + template for (auto x : s) { // OK, destructuring expansion statement + result += sizeof(x); + } + return result; +} +static_assert(f(S{}) == sizeof(int) + sizeof(short)); +\end{codeblock} +\end{example} + +\indextext{statement!expansion|)} \rSec1[stmt.jump]{Jump statements}% @@ -832,10 +1032,11 @@ A \keyword{break} statement shall be enclosed by\iref{stmt.pre} \indextext{\idxgram{iteration-statement}}% \indextext{statement!\idxcode{switch}}% -an \grammarterm{iteration-statement}\iref{stmt.iter} or -a \keyword{switch} statement\iref{stmt.switch}. +an \grammarterm{iteration-statement}\iref{stmt.iter}, +an \grammarterm{expansion-statement}\iref{stmt.expand}, +or \keyword{switch} statement\iref{stmt.switch}. The \keyword{break} statement causes -termination of the smallest such enclosing statement; +termination of the innermost such enclosing statement; control passes to the statement following the terminated statement, if any. @@ -846,45 +1047,14 @@ A \keyword{continue} statement shall be enclosed by\iref{stmt.pre} an \indextext{\idxgram{iteration-statement}}% -\grammarterm{iteration-statement}\iref{stmt.iter}. -The \keyword{continue} statement -causes control to pass to the loop-continuation portion of the -smallest such enclosing statement, that is, to the end -of the loop. More precisely, in each of the statements - -\begin{minipage}{.30\hsize} -\begin{codeblock} -while (foo) { - { - // ... - } -@\exposid{contin}@: ; -} -\end{codeblock} -\end{minipage} -\begin{minipage}{.30\hsize} -\begin{codeblock} -do { - { - // ... - } -@\exposid{contin}@: ; -} while (foo); -\end{codeblock} -\end{minipage} -\begin{minipage}{.30\hsize} -\begin{codeblock} -for (;;) { - { - // ... - } -@\exposid{contin}@: ; -} -\end{codeblock} -\end{minipage} - -a \keyword{continue} not contained in an enclosed iteration statement is -equivalent to \tcode{goto} \exposid{contin}. +\grammarterm{iteration-statement} or an \grammarterm{expansion-statement}. +If the innermost enclosing such statement $X$ +is an \grammarterm{iteration-statement}\iref{stmt.iter}, +the \keyword{continue} statement +causes control to pass to the end of the \grammarterm{statement} +or \grammarterm{compound-statement} of $X$. +Otherwise, control passes to the end of the \grammarterm{compound-statement} +of the current $S_{i}$\iref{stmt.expand}. \rSec2[stmt.return]{The \keyword{return} statement}% \indextext{\idxcode{return}}% diff --git a/source/templates.tex b/source/templates.tex index 7d4eca1376..f47cc1cd39 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -172,7 +172,9 @@ \begin{itemize} \item a template, \item an entity defined\iref{basic.def} or created\iref{class.temporary} - in a templated entity, + within the \grammarterm{compound-statement} + of an expansion statement\iref{stmt.expand}, +\item an entity defined or created in a templated entity, \item a member of a templated entity, \item an enumerator for an enumeration that is a templated entity, or \item the closure type of a \grammarterm{lambda-expression}\iref{expr.prim.lambda.closure} @@ -2591,6 +2593,9 @@ \grammarterm{noexcept-specifier}{s}. For the purpose of instantiation, the substatements of a constexpr if statement\iref{stmt.if} are considered definitions. +For the purpose of name lookup and instantiation, +the \grammarterm{compound-statement} of the \grammarterm{expansion-statement} +is considered a template definition. \pnum Because an \grammarterm{alias-declaration} cannot declare a @@ -4937,6 +4942,11 @@ and the innermost enclosing template is not instantiated, or \item no valid specialization, +ignoring \grammarterm{static_assert-declaration}s that fail, +can be generated for the \grammarterm{compound-statement} +of an expansion statement and there is no instantiation of it, or +\item +no valid specialization, ignoring \grammarterm{static_assert-declaration}{s} that fail, can be generated for a default \grammarterm{template-argument} and the default \grammarterm{template-argument} is not used in any instantiation, or @@ -5656,7 +5666,15 @@ with a result binding\iref{dcl.contract.res} of a function whose return type is dependent, \item -a \grammarterm{conversion-function-id} that specifies a dependent type, or +a \grammarterm{conversion-function-id} that specifies a dependent type, +\item +a name $N$ introduced by the \grammarterm{for-range-declaration} $D$ +of an expansion statement $S$ +if the type specified for $N$ contains a placeholder type and either +\begin{itemize} +\item the \grammarterm{expansion-initializer} of $S$ is type-dependent or +\item $S$ is not an iterating expansion statement, or +\end{itemize} \item dependent \end{itemize} @@ -5769,6 +5787,9 @@ \item it is the name of a constant template parameter, \item +it is a name introduced by the \grammarterm{for-range-declaration} +of an expansion statement\iref{stmt.expand}, +\item it names a static data member that is a dependent member of the current instantiation and is not initialized in a \grammarterm{member-declarator}, \item @@ -5916,8 +5937,6 @@ scope declaration or definition that requires the \grammarterm{noexcept-specifier}. - - \pnum For a class template specialization, a class member template specialization, or a specialization for a class member of a class template, @@ -5973,6 +5992,14 @@ different meanings according to the one-definition rule\iref{basic.def.odr}, the program is ill-formed, no diagnostic required. +\pnum +For the \grammarterm{compound-statement} +of an expansion statement\iref{stmt.expand}, +the point of instantiation is the point of instantiation +of its enclosing templated entity, if any. +Otherwise, it immediately follows the namespace-scope declaration +or definition that contains the expansion statement. + \rSec3[temp.dep.candidate]{Candidate functions} \pnum From 487912486252cd7882b9f7f94591bd3f7515aeb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Tue, 24 Jun 2025 12:02:44 +0100 Subject: [PATCH 2/2] [temp.{decls.general,dep.expr}] Editorial wording improvements * Change definite to indefinite article. * Drop unused name for a declaration. --- source/templates.tex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/templates.tex b/source/templates.tex index f47cc1cd39..fb36f92c28 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -2594,7 +2594,7 @@ For the purpose of instantiation, the substatements of a constexpr if statement\iref{stmt.if} are considered definitions. For the purpose of name lookup and instantiation, -the \grammarterm{compound-statement} of the \grammarterm{expansion-statement} +the \grammarterm{compound-statement} of an \grammarterm{expansion-statement} is considered a template definition. \pnum @@ -5668,7 +5668,7 @@ \item a \grammarterm{conversion-function-id} that specifies a dependent type, \item -a name $N$ introduced by the \grammarterm{for-range-declaration} $D$ +a name $N$ introduced by the \grammarterm{for-range-declaration} of an expansion statement $S$ if the type specified for $N$ contains a placeholder type and either \begin{itemize}