Skip to content

Commit 7dcae6f

Browse files
Dawn Perchikzygoloid
authored andcommitted
P0170R1 Wording for Constexpr Lambda
Changes to [expr.prim.lambda] 5.1.2/6 did not apply cleanly. Editorially fixed punctuation and formatting.
1 parent c27eada commit 7dcae6f

File tree

3 files changed

+123
-18
lines changed

3 files changed

+123
-18
lines changed

source/basic.tex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3615,8 +3615,9 @@
36153615
has all of the following properties:
36163616
\begin{itemize}
36173617
\item it has a trivial destructor,
3618-
\item it is an aggregate type~(\ref{dcl.init.aggr}) or has
3619-
at least one \tcode{constexpr} constructor or constructor template
3618+
\item it is either a closure type~(\ref{expr.prim.lambda}),
3619+
an aggregate type~(\ref{dcl.init.aggr}), or
3620+
has at least one \tcode{constexpr} constructor or constructor template
36203621
(possibly inherited~(\ref{namespace.udecl}) from a base class)
36213622
that is not a copy or move constructor, and
36223623
\item all of its non-static data members and base classes are

source/declarations.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@
253253
same type.
254254

255255
\pnum
256-
Each \grammarterm{decl-specifier} shall appear at most once in the complete
257-
\grammarterm{decl-specifier-seq} of a declaration, except that
256+
Each \grammarterm{decl-specifier} shall appear at most once in a complete
257+
\grammarterm{decl-specifier-seq}, except that
258258
\tcode{long} may appear twice.
259259

260260
\pnum

source/expressions.tex

Lines changed: 118 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -631,10 +631,41 @@
631631

632632
\begin{bnf}
633633
\nontermdef{lambda-declarator}\br
634-
\terminal{(} parameter-declaration-clause \terminal{)} \terminal{mutable}\opt\br
634+
\terminal{(} parameter-declaration-clause \terminal{)} decl-specifier-seq\opt\br
635635
\hspace*{\bnfindentinc}exception-specification\opt attribute-specifier-seq\opt trailing-return-type\opt
636636
\end{bnf}
637637

638+
\pnum
639+
In the \grammarterm{decl-specifier-seq} of the \grammarterm{lambda-declarator},
640+
each \grammarterm{decl-specifier}
641+
shall either be \tcode{mutable} or \tcode{constexpr}.
642+
\enterexample
643+
\begin{codeblock}
644+
auto monoid = [](auto v) { return [=] { return v; }; };
645+
auto add = [](auto m1) constexpr {
646+
auto ret = m1();
647+
return [=](auto m2) mutable {
648+
auto m1val = m1();
649+
auto plus = [=](auto m2val) mutable constexpr
650+
{ return m1val += m2val; };
651+
ret = plus(m2());
652+
return monoid(ret);
653+
};
654+
};
655+
constexpr auto zero = monoid(0);
656+
constexpr auto one = monoid(1);
657+
static_assert(add(one)(zero)() == one()); // OK
658+
659+
// Since \tcode{two} below is not declared \tcode{constexpr}, an evaluation of its \tcode{constexpr} member function call operator
660+
// cannot perform an lvalue-to-rvalue conversion on one of its subobjects (that represents its capture)
661+
// in a constant expression.
662+
auto two = monoid(2);
663+
assert(two() == 2); // OK, not a constant expression.
664+
static_assert(add(one)(one)() == two()); // ill-formed: \tcode{two()} is not a constant expression
665+
static_assert(add(one)(one)() == monoid(2)()); // OK
666+
\end{codeblock}
667+
\exitexample
668+
638669
\pnum
639670
The evaluation of a \grammarterm{lambda-expression} results in a prvalue
640671
temporary~(\ref{class.temporary}). This temporary is called the \defn{closure object}. A
@@ -652,9 +683,10 @@
652683

653684
\pnum
654685
The type of the \grammarterm{lambda-expression} (which is also the type of the
655-
closure object) is a unique, unnamed non-union class type --- called the \defn{closure
656-
type} --- whose properties are described below. This class type is neither an
657-
aggregate~(\ref{dcl.init.aggr}) nor a literal type~(\ref{basic.types}).
686+
closure object) is a unique, unnamed non-union class type
687+
--- called the \defn{closure type} ---
688+
whose properties are described below.
689+
This class type is not an aggregate type~(\ref{dcl.init.aggr}).
658690
The closure type is declared in the smallest block
659691
scope, class scope, or namespace scope that contains the corresponding
660692
\grammarterm{lambda-expression}. \enternote This determines the set of namespaces and
@@ -742,9 +774,26 @@
742774
applies to the corresponding function call operator or operator template.
743775
An \grammarterm{attribute-specifier-seq} in a \grammarterm{lambda-declarator} appertains
744776
to the type of the corresponding function call operator or operator template.
777+
The function call operator or any given operator template specialization
778+
is a constexpr function if either
779+
the corresponding \grammarterm{lambda-expression}{'s}
780+
\grammarterm{parameter-declaration-clause} is followed by \tcode{constexpr}, or
781+
it satisfies the requirements for a constexpr function~(\ref{dcl.constexpr}).
745782
\enternote Names referenced in
746783
the \grammarterm{lambda-declarator} are looked up in the context in which the
747784
\grammarterm{lambda-expression} appears. \exitnote
785+
\enterexample
786+
\begin{codeblock}
787+
auto ID = [](auto a) { return a; };
788+
static_assert(ID(3) == 3); // OK
789+
790+
struct NonLiteral {
791+
NonLiteral(int n) : n(n) { }
792+
int n;
793+
};
794+
static_assert(ID(NonLiteral{3}).n == 3); // ill-formed
795+
\end{codeblock}
796+
\exitexample
748797

749798
\pnum
750799
The closure type for a non-generic \grammarterm{lambda-expression} with no
@@ -755,9 +804,11 @@
755804
The conversion is to ``pointer to \tcode{noexcept} function''
756805
if the function call operator
757806
has a non-throwing exception specification.
758-
The
759-
value returned by this conversion function shall be the address of a function that, when
760-
invoked, has the same effect as invoking the closure type's function call operator.
807+
The value returned by this conversion function
808+
is the address of a function \tcode{F} that, when invoked,
809+
has the same effect as invoking the closure type's function call operator.
810+
\tcode{F} is a constexpr function
811+
if the function call operator is a constexpr function.
761812
For a generic lambda with no \grammarterm{lambda-capture}, the closure type has a
762813
conversion function template to
763814
pointer to function. The conversion function template has the same invented
@@ -766,7 +817,8 @@
766817
the pointer to function shall behave as if it were a
767818
\grammarterm{decltype-specifier} denoting the return type of the corresponding
768819
function call operator template specialization.
769-
\enternote If the generic lambda has no \grammarterm{trailing-return-type} or
820+
\enternote
821+
If the generic lambda has no \grammarterm{trailing-return-type} or
770822
the \grammarterm{trailing-return-type} contains a placeholder type, return type
771823
deduction of the corresponding function call operator template specialization
772824
has to be done. The corresponding specialization is that instantiation of the
@@ -794,6 +846,7 @@
794846
};
795847
\end{codeblock}
796848
\exitnote
849+
797850
\enterexample
798851
\begin{codeblock}
799852
void f1(int (*)(int)) { }
@@ -813,10 +866,13 @@
813866
int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
814867
\end{codeblock}
815868
\exitexample
869+
816870
The value returned by any given specialization of this conversion function
817-
template shall be the address of a function that, when invoked, has the same
871+
template is the address of a function \tcode{F} that, when invoked, has the same
818872
effect as invoking the generic lambda's corresponding function call operator
819873
template specialization.
874+
\tcode{F} is a constexpr function
875+
if the corresponding specialization is a constexpr function.
820876
\enternote
821877
This will result in the implicit instantiation of the generic lambda's body.
822878
The instantiated generic lambda's return type and parameter types shall match
@@ -829,9 +885,22 @@
829885
GL_int(3); // OK: same as \tcode{GL(3)}
830886
\end{codeblock}
831887
\exitexample
888+
832889
The conversion function or conversion function template is public,
833-
non-virtual, non-explicit, const, and has a non-throwing exception
890+
constexpr, non-virtual, non-explicit, const, and has a non-throwing exception
834891
specification~(\ref{except.spec}).
892+
\enterexample
893+
\begin{codeblock}
894+
auto Fwd = [](int (*fp)(int), auto a) { return fp(a); };
895+
auto C = [](auto a) { return a; };
896+
897+
static_assert(Fwd(C,3) == 3); // OK
898+
899+
// No specialization of the function call operator template can be constexpr (due to the local static).
900+
auto NC = [](auto a) { static int s; return a; };
901+
static_assert(Fwd(NC,3) == 3); // ill-formed
902+
\end{codeblock}
903+
\exitexample
835904

836905
\pnum
837906
The \grammarterm{lambda-expression}'s \grammarterm{compound-statement} yields the
@@ -1048,7 +1117,15 @@
10481117
An entity is \defnx{captured by reference}{captured!by~reference} if it is implicitly or explicitly
10491118
captured but not captured by copy. It is unspecified whether additional unnamed
10501119
non-static data members are declared in the closure type for entities captured by
1051-
reference. A member of an anonymous union shall not be captured by reference.
1120+
reference.
1121+
If declared, such non-static data members shall be of literal type.
1122+
\enterexample
1123+
\begin{codeblock}
1124+
// The inner closure type must be a literal type regardless of how reference captures are represented.
1125+
static_assert([](int n) { return [&n] { return ++n; }(); }(3) == 4);
1126+
\end{codeblock}
1127+
\exitexample
1128+
A member of an anonymous union shall not be captured by reference.
10521129

10531130
\pnum
10541131
If a \grammarterm{lambda-expression} \tcode{m2} captures an entity and that entity is
@@ -4702,9 +4779,6 @@
47024779
zero~(\ref{expr.mul}), or certain shift operations~(\ref{expr.shift})
47034780
\exitnote;
47044781

4705-
\item
4706-
a \grammarterm{lambda-expression}~(\ref{expr.prim.lambda});
4707-
47084782
\item
47094783
an lvalue-to-rvalue conversion~(\ref{conv.lval}) unless
47104784
it is applied to
@@ -4755,6 +4829,36 @@
47554829
automatic storage duration defined outside that
47564830
\grammarterm{lambda-expression}, where
47574831
the reference would be an odr-use~(\ref{basic.def.odr}, \ref{expr.prim.lambda});
4832+
\enterexample
4833+
\begin{codeblock}
4834+
void g() {
4835+
const int n = 0;
4836+
[=] {
4837+
constexpr int i = n; // OK, \tcode{n} is not odr-used and not captured here
4838+
constexpr int j = *&n; // ill-formed, \tcode{\&n} would be an odr-use of \tcode{n}
4839+
};
4840+
}
4841+
\end{codeblock}
4842+
\exitexample
4843+
\enternote
4844+
If the odr-use occurs in an invocation
4845+
of a function call operator of a closure type,
4846+
it no longer refers to \tcode{this} or to an enclosing automatic variable
4847+
due to the transformation~(\ref{expr.prim.lambda})
4848+
of the \grammarterm{id-expression} into
4849+
an access of the corresponding data member.
4850+
\enterexample
4851+
\begin{codeblock}
4852+
auto monad = [](auto v) { return [=] { return v; }; };
4853+
auto bind = [](auto m) {
4854+
return [=](auto fvm) { return fvm(m()); };
4855+
};
4856+
4857+
// OK to have captures to automatic objects created during constant expression evaluation.
4858+
static_assert(bind(monad(2))(monad)() == monad(2)());
4859+
\end{codeblock}
4860+
\exitexample
4861+
\exitnote
47584862

47594863
\item
47604864
a conversion from type \cv{} \tcode{void *} to a pointer-to-object type;

0 commit comments

Comments
 (0)