Skip to content

[expr.prim.lambda] Split specification of lambda expressions into sub… #1158

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/basic.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3634,7 +3634,7 @@
has all of the following properties:
\begin{itemize}
\item it has a trivial destructor,
\item it is either a closure type~(\ref{expr.prim.lambda}),
\item it is either a closure type~(\ref{expr.prim.lambda.closure}),
an aggregate type~(\ref{dcl.init.aggr}), or
has at least one constexpr constructor or constructor template
(possibly inherited~(\ref{namespace.udecl}) from a base class)
Expand Down
4 changes: 2 additions & 2 deletions source/declarations.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1556,7 +1556,7 @@
from an initializer. The \tcode{auto}
\grammarterm{type-specifier} is also used to
introduce a function type having a \grammarterm{trailing-return-type} or to
signify that a lambda is a generic lambda (\ref{expr.prim.lambda}).
signify that a lambda is a generic lambda (\ref{expr.prim.lambda.closure}).
The \tcode{auto} \grammarterm{type-specifier} is also used to introduce a
decomposition declaration (\ref{dcl.decomp}).

Expand All @@ -1578,7 +1578,7 @@
\grammarterm{decl-specifier}{s} in the \grammarterm{decl-specifier-seq} of a
\grammarterm{parameter-declaration} of a \grammarterm{lambda-expression}, the
\indextext{generic lambda!definition of}%
lambda is a \term{generic lambda}~(\ref{expr.prim.lambda}). \begin{example}
lambda is a \term{generic lambda}~(\ref{expr.prim.lambda.closure}). \begin{example}
\begin{codeblock}
auto glambda = [](int i, auto a) { return i; }; // OK: a generic lambda
\end{codeblock}
Expand Down
242 changes: 131 additions & 111 deletions source/expressions.tex
Original file line number Diff line number Diff line change
Expand Up @@ -601,45 +601,6 @@
\terminal{[} lambda-capture\opt{} \terminal{]}
\end{bnf}

\begin{bnf}
\nontermdef{lambda-capture}\br
capture-default\br
capture-list\br
capture-default \terminal{,} capture-list
\end{bnf}

\begin{bnf}
\nontermdef{capture-default}\br
\terminal{\&}\br
\terminal{=}
\end{bnf}

\begin{bnf}
\nontermdef{capture-list}\br
capture \terminal{...\opt}\br
capture-list \terminal{,} capture \terminal{...\opt}
\end{bnf}

\begin{bnf}
\nontermdef{capture}\br
simple-capture\br
init-capture
\end{bnf}

\begin{bnf}
\nontermdef{simple-capture}\br
identifier\br
\terminal{\&} identifier\br
\terminal{this}\br
\terminal{* this}
\end{bnf}

\begin{bnf}
\nontermdef{init-capture}\br
identifier initializer\br
\terminal{\&} identifier initializer
\end{bnf}

\begin{bnf}
\nontermdef{lambda-declarator}\br
\terminal{(} parameter-declaration-clause \terminal{)} decl-specifier-seq\opt\br
Expand All @@ -658,37 +619,6 @@
\end{codeblock}
\end{example}

\pnum
In the \grammarterm{decl-specifier-seq} of the \grammarterm{lambda-declarator},
each \grammarterm{decl-specifier}
shall either be \tcode{mutable} or \tcode{constexpr}.
\begin{example}
\begin{codeblock}
auto monoid = [](auto v) { return [=] { return v; }; };
auto add = [](auto m1) constexpr {
auto ret = m1();
return [=](auto m2) mutable {
auto m1val = m1();
auto plus = [=](auto m2val) mutable constexpr
{ return m1val += m2val; };
ret = plus(m2());
return monoid(ret);
};
};
constexpr auto zero = monoid(0);
constexpr auto one = monoid(1);
static_assert(add(one)(zero)() == one()); // OK

// Since \tcode{two} below is not declared \tcode{constexpr}, an evaluation of its \tcode{constexpr} member function call operator
// cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture)
// in a constant expression.
auto two = monoid(2);
assert(two() == 2); // OK, not a constant expression.
static_assert(add(one)(one)() == two()); // ill-formed: \tcode{two()} is not a constant expression
static_assert(add(one)(one)() == monoid(2)()); // OK
\end{codeblock}
\end{example}

\pnum
A \grammarterm{lambda-expression} is a prvalue
whose result object is called the \defn{closure object}. A
Expand All @@ -705,17 +635,43 @@
object~(\ref{function.objects}).\end{note}

\pnum
The type of the \grammarterm{lambda-expression} (which is also the type of the
closure object) is a unique, unnamed non-union class type
--- called the \defn{closure type} ---
In the \grammarterm{decl-specifier-seq} of the \grammarterm{lambda-declarator},
each \grammarterm{decl-specifier}
shall either be \tcode{mutable} or \tcode{constexpr}.

\pnum
If a \grammarterm{lambda-expression} does not include a
\grammarterm{lambda-declarator}, it is as if the \grammarterm{lambda-declarator} were
\tcode{()}.
The lambda return type is \tcode{auto}, which is replaced by the
type specified by the
\grammarterm{trailing-return-type} if provided and/or deduced from
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just "or" instead of "and/or"?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unchanged text, just moved around. Not my invention.

\tcode{return} statements as described in~\ref{dcl.spec.auto}.
\begin{example}
\begin{codeblock}
auto x1 = [](int i){ return i; }; // OK: return type is \tcode{int}
auto x2 = []{ return { 1, 2 }; }; // error: deducing return type from \grammarterm{braced-init-list}
int j;
auto x3 = []()->auto&& { return j; }; // OK: return type is \tcode{int\&}
\end{codeblock}
\end{example}

\rSec3[expr.prim.lambda.closure]{Closure types}%

\pnum
The type of a \grammarterm{lambda-expression} (which is also the type of the
closure object) is a unique, unnamed non-union class type,
called the \defn{closure type},
whose properties are described below.
This class type is not an aggregate type~(\ref{dcl.init.aggr}).

\pnum
The closure type is declared in the smallest block
scope, class scope, or namespace scope that contains the corresponding
\grammarterm{lambda-expression}. \begin{note} This determines the set of namespaces and
classes associated with the closure type~(\ref{basic.lookup.argdep}). The parameter
types of a \grammarterm{lambda-declarator} do not affect these associated namespaces and
classes. \end{note} An implementation may define the closure type differently from what
classes. \end{note} The closure type is not an aggregate type~(\ref{dcl.init.aggr}).
An implementation may define the closure type differently from what
is described below provided this does not alter the observable behavior of the program
other than by changing:

Expand All @@ -733,23 +689,6 @@
An implementation shall not add members of rvalue reference type to the closure
type.

\pnum
If a \grammarterm{lambda-expression} does not include a
\grammarterm{lambda-declarator}, it is as if the \grammarterm{lambda-declarator} were
\tcode{()}.
The lambda return type is \tcode{auto}, which is replaced by the
type specified by the
\grammarterm{trailing-return-type} if provided and/or deduced from
\tcode{return} statements as described in~\ref{dcl.spec.auto}.
\begin{example}
\begin{codeblock}
auto x1 = [](int i){ return i; }; // OK: return type is \tcode{int}
auto x2 = []{ return { 1, 2 }; }; // error: deducing return type from \grammarterm{braced-init-list}
int j;
auto x3 = []()->auto&& { return j; }; // OK: return type is \tcode{int\&}
\end{codeblock}
\end{example}

\pnum
The closure type for a non-generic \grammarterm{lambda-expression} has a public
inline function call operator~(\ref{over.call}) whose parameters and return type
Expand Down Expand Up @@ -790,7 +729,9 @@
q(); // OK: outputs \tcode{1a3.14}
\end{codeblock}
\end{example}
This function call operator or operator template is declared

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I cannot comment on unchanged code, so this comment is out of place.)

The example seems to be specific to packs only, not to the entire paragraph. What do you think of the following: In lines old-762/new-766, take the sentence "The invented type ..." out and end the paragraph after the next sentence, just before the example, Then put the removed sentence back at the start of the now new paragraph, followed by the example. That way the new paragraph concentrates on one specific detail (packs), together with example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, the example also has non-pack things, e.g. glambda at the top.
Second, "The invented type template-parameter ..." refers to the template-parameter introduced in the preceding sentence. Moving this to a separate paragraph, with something else in between, would lose the connection.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, the example is a good point. I wouldn't find losing the connection too worrying in general; I think it's acceptable for later things to refer to earlier things. That's more of a judgement call. But we can keep it as is.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but if you read the intermediate sentence, it goes off on a tangent, and returning to the template-parameter later is odd.

\pnum
The function call operator or operator template is declared
\tcode{const}~(\ref{class.mfct.non-static}) if and only if the
\grammarterm{lambda-expression}'s \grammarterm{parameter-declaration-clause} is not
followed by \tcode{mutable}. It is neither virtual nor declared \tcode{volatile}. Any
Expand Down Expand Up @@ -819,6 +760,34 @@
\end{codeblock}
\end{example}

\pnum
\begin{example}
\begin{codeblock}
auto monoid = [](auto v) { return [=] { return v; }; };
auto add = [](auto m1) constexpr {
auto ret = m1();
return [=](auto m2) mutable {
auto m1val = m1();
auto plus = [=](auto m2val) mutable constexpr
{ return m1val += m2val; };
ret = plus(m2());
return monoid(ret);
};
};
constexpr auto zero = monoid(0);
constexpr auto one = monoid(1);
static_assert(add(one)(zero)() == one()); // OK

// Since \tcode{two} below is not declared \tcode{constexpr}, an evaluation of its \tcode{constexpr} member function call operator
// cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture)
// in a constant expression.
auto two = monoid(2);
assert(two() == 2); // OK, not a constant expression.
static_assert(add(one)(one)() == two()); // ill-formed: \tcode{two()} is not a constant expression
static_assert(add(one)(one)() == monoid(2)()); // OK
\end{codeblock}
\end{example}

\pnum
The closure type for a non-generic \grammarterm{lambda-expression} with no
\grammarterm{lambda-capture}
Expand All @@ -841,6 +810,8 @@
the pointer to function shall behave as if it were a
\grammarterm{decltype-specifier} denoting the return type of the corresponding
function call operator template specialization.

\pnum
\begin{note}
If the generic lambda has no \grammarterm{trailing-return-type} or
the \grammarterm{trailing-return-type} contains a placeholder type, return type
Expand Down Expand Up @@ -891,6 +862,7 @@
\end{codeblock}
\end{example}

\pnum
The value returned by any given specialization of this conversion function
template is the address of a function \tcode{F} that, when invoked, has the same
effect as invoking the generic lambda's corresponding function call operator
Expand All @@ -910,6 +882,7 @@
\end{codeblock}
\end{example}

\pnum
The conversion function or conversion function template is public,
constexpr, non-virtual, non-explicit, const, and has a non-throwing exception
specification~(\ref{except.spec}).
Expand Down Expand Up @@ -952,6 +925,70 @@
the \grammarterm{compound-statement} of the \grammarterm{lambda-expression},
with semantics as described in~\ref{dcl.fct.def.general}.

\pnum
The closure type associated with a \grammarterm{lambda-expression} has no
default constructor and a deleted copy assignment operator. It has a
defaulted copy constructor and a defaulted move constructor~(\ref{class.copy}).
\begin{note} These special member functions are implicitly defined as
usual, and might therefore be defined as deleted. \end{note}

\pnum
The closure type associated with a \grammarterm{lambda-expression} has an
implicitly-declared destructor~(\ref{class.dtor}).

\pnum
A member of a closure type shall not be
explicitly instantiated~(\ref{temp.explicit}),
explicitly specialized~(\ref{temp.expl.spec}), or
named in a \tcode{friend} declaration~(\ref{class.friend}).

\rSec3[expr.prim.lambda.capture]{Captures}%

\begin{bnf}
\nontermdef{lambda-capture}\br
capture-default\br
capture-list\br
capture-default \terminal{,} capture-list
\end{bnf}

\begin{bnf}
\nontermdef{capture-default}\br
\terminal{\&}\br
\terminal{=}
\end{bnf}

\begin{bnf}
\nontermdef{capture-list}\br
capture \terminal{...\opt}\br
capture-list \terminal{,} capture \terminal{...\opt}
\end{bnf}

\begin{bnf}
\nontermdef{capture}\br
simple-capture\br
init-capture
\end{bnf}

\begin{bnf}
\nontermdef{simple-capture}\br
identifier\br
\terminal{\&} identifier\br
\terminal{this}\br
\terminal{* this}
\end{bnf}

\begin{bnf}
\nontermdef{init-capture}\br
identifier initializer\br
\terminal{\&} identifier initializer
\end{bnf}

\pnum
The body of a \grammarterm{lambda-expression} may refer to variables
with automatic storage duration and the \tcode{*this} object (if any)
of enclosing block scopes by capturing those entities, as described
below.

\pnum
If a \grammarterm{lambda-capture} includes a \grammarterm{capture-default} that
is \tcode{\&}, no identifier in a \grammarterm{simple-capture} of that
Expand Down Expand Up @@ -1260,23 +1297,6 @@
\end{codeblock}
\end{example}

\pnum
The closure type associated with a \grammarterm{lambda-expression} has no
default constructor and a deleted copy assignment operator. It has a
defaulted copy constructor and a defaulted move constructor~(\ref{class.copy}).
\begin{note} These special member functions are implicitly defined as
usual, and might therefore be defined as deleted. \end{note}

\pnum
The closure type associated with a \grammarterm{lambda-expression} has an
implicitly-declared destructor~(\ref{class.dtor}).

\pnum
A member of a closure type shall not be
explicitly instantiated~(\ref{temp.explicit}),
explicitly specialized~(\ref{temp.expl.spec}), or
named in a \tcode{friend} declaration~(\ref{class.friend}).

\pnum
When the \grammarterm{lambda-expression} is evaluated, the entities that are
captured by copy are used to direct-initialize each corresponding non-static data member
Expand Down Expand Up @@ -4981,7 +5001,7 @@
If the odr-use occurs in an invocation
of a function call operator of a closure type,
it no longer refers to \tcode{this} or to an enclosing automatic variable
due to the transformation~(\ref{expr.prim.lambda})
due to the transformation~(\ref{expr.prim.lambda.capture})
of the \grammarterm{id-expression} into
an access of the corresponding data member.
\begin{example}
Expand Down
2 changes: 1 addition & 1 deletion source/special.tex
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@
The first context is when a default constructor is called to initialize
an element of an array with no corresponding initializer~(\ref{dcl.init}).
The second context is when a copy constructor is called to copy an element of
an array while the entire array is copied~(\ref{expr.prim.lambda},~\ref{class.copy}).
an array while the entire array is copied~(\ref{expr.prim.lambda.capture},~\ref{class.copy}).
In either case, if the constructor has one or more default arguments,
the destruction of every temporary created in a default argument is
sequenced before the construction of the next array element, if any.
Expand Down
4 changes: 2 additions & 2 deletions source/templates.tex
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
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}~(\ref{expr.prim.lambda})
\item the closure type of a \grammarterm{lambda-expression}~(\ref{expr.prim.lambda.closure})
appearing in the declaration of a templated entity.
\end{itemize}

Expand Down Expand Up @@ -4796,7 +4796,7 @@
the same template parameters and the same access as that of the function template
\tcode{f}
used at that point, except that the scope in which a closure type is
declared~(\ref{expr.prim.lambda}) -- and therefore its associated namespaces --
declared~(\ref{expr.prim.lambda.closure}) -- and therefore its associated namespaces --
remain as determined from the context of the definition for the default
argument.
This analysis is called
Expand Down