From 5ccf2cfcb82a0a9384d4f034cc64ba7422d7d1e6 Mon Sep 17 00:00:00 2001 From: Jens Maurer Date: Wed, 27 Nov 2024 13:35:16 +0100 Subject: [PATCH] P1061R10 Structured Bindings can introduce a Pack Editorial notes: * Replace digits with numeral words in comment in example. --- source/declarations.tex | 95 ++++++++++++++++++++++++++++++++--------- source/expressions.tex | 3 ++ source/preprocessor.tex | 2 +- source/templates.tex | 51 +++++++++++++++++++++- 4 files changed, 129 insertions(+), 22 deletions(-) diff --git a/source/declarations.tex b/source/declarations.tex index 83913867c3..596c0c4e61 100644 --- a/source/declarations.tex +++ b/source/declarations.tex @@ -68,19 +68,19 @@ \end{bnf} \begin{bnf} -\nontermdef{attributed-identifier}\br - identifier \opt{attribute-specifier-seq} +\nontermdef{sb-identifier}\br + \opt{\terminal{...}} identifier \opt{attribute-specifier-seq} \end{bnf} \begin{bnf} -\nontermdef{attributed-identifier-list}\br - attributed-identifier\br - attributed-identifier-list \terminal{,} attributed-identifier +\nontermdef{sb-identifier-list}\br + sb-identifier\br + sb-identifier-list \terminal{,} sb-identifier \end{bnf} \begin{bnf} \nontermdef{structured-binding-declaration}\br - \opt{attribute-specifier-seq} decl-specifier-seq \opt{ref-qualifier} \terminal{[} attributed-identifier-list \terminal{]} + \opt{attribute-specifier-seq} decl-specifier-seq \opt{ref-qualifier} \terminal{[} sb-identifier-list \terminal{]} \end{bnf} \begin{bnf} @@ -218,6 +218,10 @@ \tcode{thread_local}, \tcode{auto}\iref{dcl.spec.auto}, or a \grammarterm{cv-qualifier}. +The declaration shall contain at most one \grammarterm{sb-identifier} +whose \grammarterm{identifier} is preceded by an ellipsis. +If the declaration contains any such \grammarterm{sb-identifier}, +it shall declare a templated entity\iref{temp.pre}. \begin{example} \begin{codeblock} template concept C = true; @@ -7040,13 +7044,17 @@ \pnum A structured binding declaration introduces the \grammarterm{identifier}{s} -$\tcode{v}_0$, $\tcode{v}_1$, $\tcode{v}_2, \dotsc$ +$\tcode{v}_0$, $\tcode{v}_1$, $\tcode{v}_2, \dotsc, \tcode{v}_{N-1}$ of the -\grammarterm{attributed-identifier-list} as names -of \defn{structured binding}{s}. +\grammarterm{sb-identifier-list} as names. +An \grammarterm{sb-identifier} that contains an ellipsis +introduces a structured binding pack\iref{temp.variadic}. +A \defn{structured binding} is either +an \grammarterm{sb-identifier} that does not contain an ellipsis or +an element of a structured binding pack. The optional \grammarterm{attribute-specifier-seq} of -an \grammarterm{attributed-identifier} -appertains to the structured binding so introduced. +an \grammarterm{sb-identifier} +appertains to the associated structured bindings. Let \cv{} denote the \grammarterm{cv-qualifier}{s} in the \grammarterm{decl-specifier-seq} and \placeholder{S} consist of @@ -7080,6 +7088,42 @@ \tcode{E} is never a reference type\iref{expr.prop}. \end{note} +\pnum +The \defn{structured binding size} of \tcode{E}, as defined below, +is the number of structured bindings +that need to be introduced by the structured binding declaration. +If there is no structured binding pack, +then the number of elements in the \grammarterm{sb-identifier-list} +shall be equal to the structured binding size of \tcode{E}. +Otherwise, the number of non-pack elements shall be no more than +the structured binding size of \tcode{E}; +the number of elements of the structured binding pack is +the structured binding size of \tcode{E} less +the number of non-pack elements in the\grammarterm{sb-identifier-list}. + +\pnum +Let $\textrm{SB}_i$ denote +the $i^\textrm{th}$ structured binding in the structured binding declaration +after expanding the structured binding pack, if any. +\begin{note} +If there is no structured binding pack, +then $\textrm{SB}_i$ denotes $\tcode{v}_i$. +\end{note} +\begin{example} +\begin{codeblock} +struct C { int x, y, z; }; + +template +void now_i_know_my() { + auto [a, b, c] = C(); // OK, $\textrm{SB}_0$ is \tcode{a}, $\textrm{SB}_1$ is \tcode{b}, and $\textrm{SB}_2$ is \tcode{c} + auto [d, ...e] = C(); // OK, $\textrm{SB}_0$ is \tcode{d}, the pack \tcode{e} $(\tcode{v}_1)$ contains two structured bindings: $\textrm{SB}_1$ and $\textrm{SB}_2$ + auto [...f, g] = C(); // OK, the pack \tcode{f} $(\tcode{v}_0)$ contains two structured bindings: $\textrm{SB}_0$ and $\textrm{SB}_1$, and $\textrm{SB}_2$ is \tcode{g} + auto [h, i, j, ...k] = C(); // OK, the pack \tcode{k} is empty + auto [l, m, n, o, ...p] = C(); // error: structured binding size is too small +} +\end{codeblock} +\end{example} + \pnum If a structured binding declaration appears as a \grammarterm{condition}, the decision variable\iref{stmt.pre} of the condition is \exposid{e}. @@ -7090,9 +7134,10 @@ the program is ill-formed. \pnum -If \tcode{E} is an array type with element type \tcode{T}, the number -of elements in the \grammarterm{attributed-identifier-list} shall be equal to the -number of elements of \tcode{E}. Each $\tcode{v}_i$ is the name of an +If \tcode{E} is an 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 lvalue that refers to the element $i$ of the array and whose type is \tcode{T}; the referenced type is \tcode{T}. \begin{note} @@ -7103,6 +7148,19 @@ auto f() -> int(&)[2]; auto [ x, y ] = f(); // \tcode{x} and \tcode{y} refer to elements in a copy of the array return value auto& [ xr, yr ] = f(); // \tcode{xr} and \tcode{yr} refer to elements in the array referred to by \tcode{f}'s return value + +auto g() -> int(&)[4]; + +template +void h(int (&arr)[N]) { + auto [a, ...b, c] = arr; // \tcode{a} names the first element of the array, \tcode{b} is a pack referring to the second and + // third elements, and \tcode{c} names the fourth element + auto& [...e] = arr; // \tcode{e} is a pack referring to the four elements of the array +} + +void call_h() { + h(g()); +} \end{codeblock} \end{example} @@ -7113,8 +7171,7 @@ the expression \tcode{std::tuple_size::value} shall be a well-formed integral constant expression and -the number of elements in -the \grammarterm{attributed-identifier-list} shall be equal to the value of that +the structured binding size of \tcode{E} is equal to the value of that expression. Let \tcode{i} be an index prvalue of type \tcode{std::size_t} corresponding to $\tcode{v}_i$. @@ -7144,7 +7201,7 @@ \placeholder{S} \terminal{U$_i$ r$_i$ =} initializer \terminal{;} \end{ncbnf} -Each $\tcode{v}_i$ is the name of an lvalue of type $\tcode{T}_i$ +Each $\textrm{SB}_i$ is the name of an lvalue of type $\tcode{T}_i$ that refers to the object bound to $\tcode{r}_i$; the referenced type is $\tcode{T}_i$. The initialization of \exposid{e} and @@ -7162,12 +7219,12 @@ well-formed when named as \tcode{\exposidnc{e}.\placeholder{name}} in the context of the structured binding, \tcode{E} shall not have an anonymous union member, and -the number of elements in the \grammarterm{attributed-identifier-list} shall be +the structured binding size of \tcode{E} is equal to the number of non-static data members of \tcode{E}. Designating the non-static data members of \tcode{E} as $\tcode{m}_0$, $\tcode{m}_1$, $\tcode{m}_2, \dotsc$ (in declaration order), -each $\tcode{v}_i$ is the +each $\textrm{SB}_i$ is the name of an lvalue that refers to the member \tcode{m}$_i$ of \exposid{e} and whose type is that of \tcode{\exposidnc{e}.$\tcode{m}_i$}\iref{expr.ref}; diff --git a/source/expressions.tex b/source/expressions.tex index ad5e7524fa..b063e7453d 100644 --- a/source/expressions.tex +++ b/source/expressions.tex @@ -2864,6 +2864,9 @@ } \end{codeblock} \end{example} + +\pnum +A fold expression is a pack expansion. \indextext{expression!fold|)}% \rSec2[expr.prim.req]{Requires expressions} diff --git a/source/preprocessor.tex b/source/preprocessor.tex index b1676c5ebd..c030cae071 100644 --- a/source/preprocessor.tex +++ b/source/preprocessor.tex @@ -1905,7 +1905,7 @@ \defnxname{cpp_sized_deallocation} & \tcode{201309L} \\ \rowsep \defnxname{cpp_static_assert} & \tcode{202306L} \\ \rowsep \defnxname{cpp_static_call_operator} & \tcode{202207L} \\ \rowsep -\defnxname{cpp_structured_bindings} & \tcode{202403L} \\ \rowsep +\defnxname{cpp_structured_bindings} & \tcode{202411L} \\ \rowsep \defnxname{cpp_template_template_args} & \tcode{201611L} \\ \rowsep \defnxname{cpp_threadsafe_static_init} & \tcode{200806L} \\ \rowsep \defnxname{cpp_unicode_characters} & \tcode{200704L} \\ \rowsep diff --git a/source/templates.tex b/source/templates.tex index dbe304d448..afee3d6b0b 100644 --- a/source/templates.tex +++ b/source/templates.tex @@ -2840,11 +2840,27 @@ \end{codeblock} \end{example} +\pnum +A \defn{structured binding pack} is an \grammarterm{sb-identifier} +that introduces zero or more structured bindings\iref{dcl.struct.bind}. +\begin{example} +\begin{codeblock} +auto foo() -> int(&)[2]; + +template +void g() { + auto [...a] = foo(); // \tcode{a} is a structured binding pack containing two elements + auto [b, c, ...d] = foo(); // \tcode{d} is a structured binding pack containing zero elements +} +\end{codeblock} +\end{example} + \pnum A \defn{pack} is a template parameter pack, a function parameter pack, -or an \grammarterm{init-capture} pack. +an \grammarterm{init-capture} pack, or +a structured binding pack. The number of elements of a template parameter pack or a function parameter pack is the number of arguments provided for the parameter pack. @@ -3007,7 +3023,14 @@ designating the variable introduced by the $i^\text{th}$ \grammarterm{init-capture} that resulted from instantiation of -the \grammarterm{init-capture} pack declaration. +the \grammarterm{init-capture} pack declaration; +otherwise + +\item +if the pack is a structured binding pack, +the element is an \grammarterm{id-expression} +designating the $i^\textrm{th}$ structured binding in the pack +that resulted from the structured binding declaration. \end{itemize} When $N$ is zero, the instantiation of a pack expansion does not alter the syntactic interpretation of the enclosing construct, @@ -5338,6 +5361,28 @@ a structured binding declaration\iref{dcl.struct.bind} whose \grammarterm{brace-or-equal-initializer} is type-dependent, \item +associated by name lookup with a pack, +\begin{example} +\begin{codeblock} +struct C { }; + +void g(...); // \#1 + +template +void f() { + C arr[1]; + auto [...e] = arr; + g(e...); // calls \#2 +} + +void g(C); // \#2 + +int main() { + f(); +} +\end{codeblock} +\end{example} +\item associated by name lookup with an entity captured by copy\iref{expr.prim.lambda.capture} in a \grammarterm{lambda-expression} @@ -5522,6 +5567,8 @@ \keyword{sizeof} \terminal{...} \terminal{(} identifier \terminal{)}\br fold-expression \end{ncsimplebnf} +unless the \grammarterm{identifier} is a structured binding pack +whose initializer is not dependent. \pnum A \grammarterm{noexcept-expression}\iref{expr.unary.noexcept}