Yet another approach for constrained declarations (original) (raw)
Document number: P1141R2
Ville Voutilainen
Thomas Köppe
Andrew Sutton
Herb Sutter
Gabriel Dos Reis
Bjarne Stroustrup
Jason Merrill
Hubert Tong
Eric Niebler
Casey Carter
Tom Honermann
Erich Keane
Walter E. Brown
Michael Spertus
Richard Smith
2018-11-09
Abstract
We propose a short syntax for the constrained declaration of function parameters, function return types and variables. The new syntax is a “constrainedauto
”, e.g. void sort(Sortable auto& c);
.
Contents
Revision history
- P1141R2 (this document): Added formal wording for parts 1, 3, and 4. Parts 2 and 5 are not being proposed.
- P1141R1: Added discussion in Parts 2 and 5 on _return-type-requirement_s and in Part 4 on variadic concepts.
- P1141R0: Initial proposal.
Proposal summary
This paper proposes three things:
- A syntax for constrained declarations that is practically a “constrained
auto
”; the principle being “whereverauto
goes, aConstraint auto
can also (non-recursively) go”. The semantics are to deduce likeauto
and additionally check a constraint. In a nutshell,void f(Sortable auto x); Sortable auto f(); // #1 Sortable auto x = f(); // #2 template <Sortable auto N> void f();
and all combined:
template <Sortable auto N> Sortable auto f(Sortable auto x) { Sortable auto y = init; }
An unconstrained version of that is:
template <auto N> auto f(auto x) { auto y = init; }
So, this proposal includes
auto
-typed parameters for functions, which we already allow for lambdas. - Simplifying (and thus restricting) the rules in[temp.param]/10, so that
template <Sortable S>
always means thatS
is a type parameter, andtemplate <Sortable auto S>
always means thatS
is a non-type parameter. Template template-parameters are no longer supported in this short form. Moreover,Sortable
is restricted to be a concept that takes a type parameter or type parameter pack; non-type and template concepts are no longer supported in this short form. - Changing the meaning of parameter packs, so that
template <Sortable ...T>
meansrequires Sortable<T> && ... && true
, and notrequires Sortable<T...>
.
Sortable
is a “type concept” in all the examples of this summary.
This paper specifically does not propose
- any new lead-in syntax for templates, or
- a new syntax for introducing names for placeholder types, or
- a shortcut syntax for applying multiple constraints to a placeholder type.
The idea of this approach is to provide a syntax that
- works for constrained function parameters, constrained return types, constrained variables, and type-constrained non-type template parameters;
- avoids inventing many adventurous new things;
- in particular, avoids inventing new type sigils;
- does not clash with explicit template instantiations; and
- is compatible with what we already have in polymorphic lambdas, and makes functions uniform with them.
The previous revision of this paper (P1141R1) also proposed (in Part 2) an optional relaxation where the auto
would be optional for the cases #1 and #2 illustrated above, and (in Part 5) a change of the meaning of -> Concept auto
. However, EWG decided to propose only parts 1, 3, and 4.
Proposal details
Part 1: “Constrained auto
”
The approach proposed here borrows a subset ofP0807R0 An Adjective Syntax for Concepts. The idea is that we don’t try to come up with a notation that does everything that P0807 does; in particular, there is no proposal for a new syntax to introduce a type name.
Function templates
The approach is simple: allow auto
parameters to produce function templates (as they produce polymorphic lambdas), and allow the auto
to be preceded by a concept name. In every case, such a parameter is a deduced parameter, and we can see which parameters are deduced and which ones are not:
[](auto a, auto& b, const auto& c, auto&& d) {...}; // unconstrained [](Constraint auto a, Constraint auto& b, const Constraint auto& c, Constraint auto&& d) {...}; // constrained void f1(auto a, auto& b, const auto& c, auto&& d) {...}; // unconstrained void f2(Constraint auto a, Constraint auto& b, const Constraint auto& c, Constraint auto&& d) {...}; // constrained [](Constraint auto&& a, SomethingElse&& b) {...}; // a constrained deduced forwarding reference and a concrete rvalue reference void f3(Constraint auto&& a, SomethingElse&& b) {...}; // a constrained deduced forwarding reference and a concrete rvalue reference
The appearance of auto
(including Constraint auto
) in a parameter list tells us that we are dealing with a function template. For each parameter, we know whether it is deduced or not. We can tell apart concepts from types: concepts precede auto
, types do not.
Return types and variable declarations
Constrained return types work the same way:
auto f4(); // unconstrained, deduced. Constraint auto f5(); // constrained, deduced. Whatever f6(); // See part 2. If Whatever is a type, not deduced. // If Whatever is a concept, constrained and deduced.
Note that f4
, f5
and f6
are not templates (whereas the previous f1
, f2
and f3
are templates). Here, there is no mention of auto
in the parameter list. Users have the choice of adopting a style where it is explicit as to whether the return type is deduced.
Constrained types for variables work the same way:
auto x1 = f1(); // unconstrained, deduced. Constraint auto x2 = f2(); // constrained, deduced. Whatever x3 = f3(); // See part 2. If Whatever is a type, not deduced. // If Whatever is a concept, constrained and deduced.
Again, users can make it so that it is easy to see when deduction occurs.
Since non-type template parameters can be deduced via auto
(as in template <auto N> void f();
), we also allow a constraint there:
template <Constraint auto N> void f7();
Note, however, that this can only be a type constraint; non-type concepts (including auto concepts) are not allowed in this form.
Other uses of auto
In concert with the general approach that “Constraint auto
goes whereverauto
goes”, new-expressions and conversion operators work:
auto alloc_next() { return new Sortable auto(this->next_val()); } operator Sortable auto() { }
A “Constraint auto
” cannot be used to indicate that a function declarator has a trailing return type:
Constraint auto f() -> auto; // ill-formed; shall be the single type-specifier auto
decltype(auto)
can also be constrained:
auto f() -> Constraint decltype(auto); Constraint decltype(auto) x = f();
Structured bindings do deduce auto
in some cases; however, the auto
is deduced from the whole (and not from the individual components). It is somewhat doubtful that applying the constraint to the whole, as opposed to (for example) applying separately to each component, is the correct semantic. Therefore, we propose to defer enabling the application of constraints to structured bindings to separate papers.
General rules
The constraint applies directly to the deduced type. It does not apply to the possibly cv-qualified type described by the type specifiers, nor does it apply to the type declared for the variable:
const Assignable<int> auto&& c = *static_cast<int *>(p); // Assignable<int &, int>
Naturally, if the deduced type is cv-qualified (or a reference), the constraint applies to that type.
To keep things simple, an auto
(or decltype(auto)
) being constrained is always immediately preceded by the constraint. So, cv-qualifiers and concept-identifiers cannot be freely mixed:
const Contraint auto x = foo(); // ok Constraint const auto x = foo(); // ill-formed Constraint auto const y = foo(); // ok
We propose only the ability to apply one single constraint for a parameter, return type, or non-type template parameter. Any proposal to consider multiple constraints should happen separately after C++20.
Partial concept identifiers also work. Given a concepttemplate <typename T, typename... Args> concept Constructible = /* ... */;
, we can say:
void f(Constructible<int> auto x); // Constructible<decltype(x), int> is satisfied Constructible<int> auto f(); Constructible<int> auto x = f(); template <Constructible<int> auto N> void f();
Part 2: Relaxed “constrained auto
” [not proposed]
Part 3: Meaning of “template <Concept T>
”
In [temp.param]/10 we have:
A constrained-parameter declares a template parameter whose kind (type, non-type, template) and type match that of the prototype parameter (17.6.8) of the concept designated by the type-constraint in the constrained-parameter. Let
X
be the prototype parameter of the designated concept. The declared template parameter is determined by the kind ofX
(type, non-type, template) and the optional ellipsis in the constrained-parameter as follows.
- If
X
is a type template-parameter, the declared parameter is a type template-parameter.- If
X
is a non-type template-parameter, the declared parameter is a non-type template-parameter having the same type asX
.- If
X
is a template template-parameter, the declared parameter is a template template-parameter having the same template-parameter-list asX
, excluding default template arguments.- If the type-constraint is followed by an ellipsis, then the declared parameter is a template parameter pack (17.6.3).
[Example:
template<typename T> concept C1 = true; template<template<typename> class X> concept C2 = true; template<int N> concept C3 = true; template<typename... Ts> concept C4 = true; template<char... Cs> concept C5 = true; template<C1 T> void f1(); // OK, T is a type template-parameter template<C2 X> void f2(); // OK, X is a template with one type-parameter template<C3 N> void f3(); // OK, N has type int template<C4... Ts> void f4(); // OK, Ts is a template parameter pack of types template<C4 T> void f5(); // OK, T is a type template-parameter template<C5... Cs> void f6(); // OK, Cs is a template parameter pack of chars
—_end example_]
Does that seem like a mouthful?
That’s because it is. In template <Constraint T>
, the kind ofT
depends on the kind of the prototype parameter of Constraint
.
We instead propose that, for such a constrained-parameter syntax:
T
should always be a type, andConstraint
would always need to be a concept that has a corresponding type parameter or type parameter pack.
To be clear, we are not proposing that concepts in general should not have non-type or template template parameters. We are merely proposing for it to be the case that the constrained parameter shortcut is not provided for concepts with such prototype parameters; such concepts would need to be used with a requires-clause. The constrained parameter syntax should mean just one thing. Note that the same syntax template <A T>
is still a non-type parameter when A
is a type name rather than a concept. We are willing to tolerate this small potential for ambiguity.
The rationale for this part is as follows:
- It seems desirable to have the constrained template parameter syntax.
- It would be nice if that syntax covered the most common case.
- It would further be nice if that syntax covered only the most common case.
- The other cases are expected to be so rare that there’s no need to provide a shortcut for them, and they are certainly rare enough that they shouldn’t use the same syntax.
So, to clarify:
template <MyIntTypeDef N>
means a non-type parameter, like it always did.template <ConceptName T>
means a type parameter constrained byConceptName
, and the prototype parameter ofConceptName
needs to be a type parameter or a type parameter pack.template <auto N>
means a non-type parameter with a deduced type.template <ConceptName auto N>
means a non-type parameter with a deduced type constrained byConceptName
, and the prototype parameter ofConceptName
needs to be a type parameter or a type parameter pack.
Other use cases can be done with _requires-clause_s.
Part 4: Meaning of “template <Concept... T>
” and its friends
In [temp.param]/11 we have:
template<C2... T> struct s3; // associates C2<T...>
This seems to be doing an unexpected thing, which is having the constraint apply to more than one type in a pack at a time. We propose that, regardless of whether the prototype parameter of the named concept is a pack:
- For a simple pack of constrained types, the concept mentioned is applied, as a unary concept, to each type in the pack in turn.
- For a pack of constrained types that use _partial-concept-id_s, the concept mentioned is applied, as an n-ary concept whose arity is unaffected by the size of the pack, individually to each type in the pack in turn.
In other words,
template <ConceptName... T> void f(T...);
means a variadic function template where each type in the packT
needs to satisfyConceptName
as a unary concept, applied asConceptName<Tn>
.- Similarly,
void f(ConceptName auto... T);
means exactly the same thing. template <ConceptName<int>... U> void f(U...);
means a variadic function template where each type in the packU
needs to satisfyConceptName
as a binary concept, applied asConceptName<Un, int>
.- Similarly,
void f(ConceptName<int> auto... U);
means exactly the same thing. template <ConceptName<0u, void, wchar_t>... U> void f(U...);
means a variadic function template where each type in the packU
needs to satisfyConceptName
as a n-ary concept, applied asConceptName<Un, 0u, void, wchar_t>
.
Part 5: Meaning of “-> Concept auto
” and its friends [not proposed]
Proposed wording for Parts 1, 3, and 4
Changes in [expr]
Update [expr.prim.lambda, 7.5.5], paragraph 5, to allow placeholder type specifiers as lambda parameters.
A lambda is a generic lambda if
thethere is a decl-specifier that is a placeholder-type-specifier in the decl-specifier-seq of a parameter-declaration of the lambda-expression, or if the lambda has a template-parameter-list. [Example: […] —_end example_]auto
type-specifier appears as one of the_decl-specifier_s
In [expr.prim.lambda.closure, 7.5.5.1], modify paragraph 3.
The closure type for a
non-genericlambda-expression has a public inline function call operator (for a non-generic lambda) or function call operator template (for a generic lambda) (11.5.4) whose parameters and return type are described by the lambda-expression_’s parameter-declaration-clause and trailing-return-type respectively. For a generic lambda, the closure type has a public inline function call operator member template (12.6.2), and whose template-parameter-list consists of the specified template-parameter-list, if any, to which is appended one invented type template-parameter for each occurrence of. The requires-clause of the function call operator template is the requires-clause immediately followingauto
in the lambda’s parameter-declaration-clause, in order of appearance. The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack (9.2.3.5). The return type and function parameters of the function call operator template are derived from the lambda-expression_’s trailing-return-type and parameter-declaration-clause by replacing each occurrence ofauto
in the decl-specifier_s of the_parameter-declaration-clause with the name of the corresponding_invented template-parameter<
template-parameter-list>
, if any. The trailing_requires-clause of the function call operator or operator template is the requires-clause following the lambda-declarator, if any.[Note: The function call operator for a generic lambda might be an abbreviated function template (9.2.3.5). —_end note_] [Example: […] —_end example_]
Modify paragraph 6 as follows.
[Note: The function call operator or operator template may be constrained (12.4.2) by a
constrained-parametertype-constraint(12.1), a requires-clause (Clause 12), or a trailing requires-clause (9.2). [Example:template <typename T> concept C1 = /* ... */; template <std::size_t N> concept C2 = /* ... */; template <typename A, typename B> concept C3 = /* ... */; auto f = []<typename T1, C1 T2> requires C2<sizeof(T1) + sizeof(T2)> (T1 a1, T1 b1, T2 a2, auto a3, auto a4) requires C3<decltype(a4), T2> { // T2 is a constrained parameterconstrained by a type-constraint, // T1 and T2 are constrained by a requires-clause, and // T2 and the type of a4 are constrained by a trailing requires-clause. };
—_end example_] —_end note_]
Changes in [dcl]
Change [dcl.type.simple, 9.1.7.2] paragraph 1 to add _placeholder-type-specifier_s.
simple-type-specifier: nested-name-specifieropt type-name nested-name-specifier template simple-template-id nested-name-specifieropt template-name char char16_t char32_t wchar_t bool short int long signed unsigned float double void auto decltype-specifier placeholder-type-specifier type-name: class-name enum-name typedef-name simple-template-id decltype-specifier: decltype ( expression ) decltype ( auto ) placeholder-type-specifier: type-constraintopt auto type-constraintopt decltype ( auto )
Modify paragraph 2 as follows.
The simple-type-specifierA_placeholder-type-specifier_ is a placeholder for a type to be deduced (9.1.7.4).auto
Add _placeholder-type-specifier_s to the table of_simple-type-specifier_s and their meaning.
Specifier(s) Type type-name the type named simple-template-id the as defined in 12.2 ... ... void “void” autoplaceholder for a type to be deduceddecltype(auto)placeholder for a type to be deduceddecltype(expression) the type as described below placeholder-type-specifier placeholder for a type to be deduced
In [dcl.spec.auto, 9.1.7.4], modify and split paragraph 1 as follows.
TheA _placeholder-type-specifier_designates a placeholder type that will be replaced later by deduction from an initializer.auto
anddecltype(auto)
_type-specifier_s are used toA placeholder-type-specifier of the form type-constraint_opt
auto
can be used in the decl-specifier-seq of a parameter-declaration of a function declaration or lambda-expression and signifies that the function is an abbreviated function template (9.2.3.5) or the~~Theauto
type-specifier is also used to introduce a function type having a_trailing-return-type or to signify that a~~ lambda is a generic lambda (7.5.5).Theauto
type-specifier is also used to introduce a structured binding declaration (9.5).
Modify (old) paragraph 3 as follows.
The type of a variable declared using
a placeholder type is deduced from its initializer. This use is allowed in an initializing declaration (9.3) of a variable.auto
ordecltype(auto)
The placeholder type shall appear as one of the_decl-specifier_s in the decl-specifier-seq and the_decl-specifier-seq_ shall be followed by one or more declarators, each of which shall be followed by a non-empty initializer. […]—_end example_]Theauto
ordecltype(auto)
auto
type-specifier can also be used to introduce a structured binding declaration (9.5).
Modify (old) paragraph 5 as follows.
A program that uses
a placeholder type in a context not explicitly allowed in this subclause is ill-formed.auto
ordecltype(auto)
In [dcl.type.auto.deduct, 9.1.7.4.1], modify the last sentence of paragraph 2 as follows.
[…] In the case of a return statement with no operand or with an operand of type
void
,T
shall be either_type-constraint_optdecltype(auto)
or_cv_ _type-constraint_optauto
.
Modify paragraph 4 as follows.
If the
placeholder is theplaceholder-type-specifier is of the form_type-constraint_optauto
type-specifierauto
, the deduced typeT′
replacingT
is determined using the rules for template argument deduction. ObtainP
fromT
by replacing the occurrences of _type-constraint_optauto
with either a new invented type template parameterU
or, if the initialization is copy-list-initialization, withstd::initializer_list<U>
. […]
Modify paragraph 5 as follows.
If the
placeholder is theplaceholder-type-specifier is of the form_type-constraint_optdecltype(auto)
type-specifierdecltype(auto)
,T
shall be the placeholder alone. The type deduced forT
is determined […]
Append a new paragraph as follows.
?. For a placeholder-type-specifier with a type-constraint, if the type deduced for the placeholder does not satisfy its immediately-declared constraint ([temp, 12]), the program is ill-formed.
Add the following paragraphs to [dcl.fct, 9.2.3.5], after paragraph 16.
?. An abbreviated function template is a function declaration whose parameter-type-list includes one or more placeholders (9.1.7.4). An abbreviated function template is equivalent to a function template (17.6.5) whose template-parameter-list includes one invented type_template-parameter_ for each occurrence of a placeholder type in the_decl-specifier-seq_ of a parameter-declaration in the function’s parameter-type-list, in order of appearance. For a placeholder-type-specifier of the form
auto
, the invented parameter is an unconstrained type-parameter. For a placeholder-type-specifier of the form type-constraintauto
, the invented parameter is a type-parameter with that type-constraint. The invented type template-parameter is a template parameter pack if the corresponding parameter-declaration declares a function parameter pack (9.2.3.5). If the placeholder containsdecltype(auto)
, the program is ill-formed. The adjusted function parameters of an abbreviated function template are derived from the parameter-declaration-clause by replacing each occurrence of a placeholder with the name of the corresponding invented_template-parameter_.[Example:
template<typename T> concept C1 = /* ... */; template<typename T> concept C2 = /* ... */; template<typename... Ts> concept C4 = /* ... */; void g1(const C1 auto*, C2 auto&); void g3(C1 auto&...); void g5(C4 auto...); void g7(C4 auto);
These declarations are functionally equivalent (but not equivalent) to the following declarations.
template<C1 T, C2 U> void g1(const T*, U&); template<C1... Ts> void g3(Ts&...); template<C4... Ts> void g5(Ts...); template<C4 T> void g7(T);
Abbreviated function templates can be specialized like all function templates.
template<> void g1<int>(const int*, const double&); // OK, specialization of g1<int, const double>
—_end example_]
?. An abbreviated function template can have a template-head. The invented template-parameter_s are appended to the_template-parameter-list after the explicitly declared_template-parameter_s.
[Example:
template<typename> concept C = /* ... */; template <typename T, C U> void g(T x, U y, C auto z);
This is functionally equivalent to each of the following two declarations.
template<typename T, C U, C W> void g(T x, U y, W z); template<typename T, typename U, typename W> requires C<U> && C<W> void g(T x, U y, W z);
—_end example_]
?. A function declaration at block scope shall not declare an abbreviated function template.
Changes in [temp]
Add to the grammar in [temp, 12] the following.
concept-name: identifier type-constraint: nested-name-specifieropt concept-name nested-name-specifieropt concept-name < template-argument-listopt >
Append a new paragraph as follows.
?. A type-constraint
Q
that designates a conceptC
can be used to constrain a contextually-determined type or template type parameter packT
with a constraint-expressionE
defined as follows. IfQ
is of the formC<A1, ..., An>
, then letE′
beC<T, A1, ..., An>
. Otherwise, letE′
beC<T>
. IfT
is not a pack, thenE
isE′
, otherwiseE
is(E′ && ...)
. This is called the immediately-declared constraint ofT
. The concept designated by a type-constraint shall be a type concept (12.6.8).
Change [temp.param, 12.1] paragraph 1 to remove the grammar for_constrained-parameter_ and to enhance the grammar of_type-parameter_.
Editorial note: No further appearances of “_qualified-concept-name_” should remain in the working draft after application of P1084R2 and P1141R2 (this paper).
template-parameter: type-parameter parameter-declaration constrained-parameter type-parameter: type-parameter-key ...opt identifieropt type-parameter-key identifieropt = type-id type-constraint ...opt identifieropt type-constraint identifieropt = type-id template-head type-parameter-key ...opt identifieropt template-head type-parameter-key identifieropt = id-expression type-parameter-key: class typename constrained-parameter: qualified-concept-name ... identifieropt qualified-concept-name identifieropt default-template-argumentopt qualified-concept-name: nested-name-specifieropt concept-name nested-name-specifieroptpartial-concept-id partial-concept-id: concept-name < template-argument-listopt >
Change [12.1, temp.param] paragraph 9 as follows.
A partial-concept-id is a concept-name followed by a sequence of template-arguments. These template arguments are used to form a constraint-expression as described below.A type-parameter that starts with a_type-constraint_ introduces the immediately-declared constraint of the parameter.
Delete [12.1, temp.param] paragraph 10.
A constrained-parameter declares a template parameter whose kind (type, non-type, template) and type match that of the prototype parameter (12.6.8) of the concept designated by the qualified-concept-name in the constrained-parameter. LetX
be the prototype parameter of the designated concept. The declared template parameter is determined by the kind ofX
(type, non-type, template) and the optional ellipsis in the constrained-parameter as follows.
IfX
is a type template-parameter, the declared parameter is a type template-parameter.IfX
is a non-type template-parameter, the declared parameter is a non-type template-parameter having the same type asX
.IfX
is a template template-parameter, the declared parameter is a template template-parameter having the same template-parameter-list asX
, excluding default template arguments.If the qualified-concept-name is followed by an ellipsis, then the declared parameter is a template parameter pack (temp.variadic, 12.6.3).
[Example:`~~template concept C1 = true;~~
template<template class X> concept C2 = true;template concept C3 = true;template<typename... Ts> concept C4 = true;template<char... Cs> concept C5 = true;
template void f1(); // OK, T is a type template-parametertemplate void f2(); // OK, X is a template with one type-parametertemplate void f3(); // OK, N has type inttemplate<C4... Ts> void f4(); // OK, Ts is a template parameter pack of typestemplate void f5(); // OK, T is a type template-parametertemplate<C5... Cs> void f6(); // OK, Cs is a template parameter pack of chars`
—_end example_]
In [12.1, temp.param], delete the normative wording of (old) paragraph 11 and merge the (modified) example into paragraph 9 as follows.
Editorial note: This change effects the design change of Part 4 (changing the meaning of ...
). The new pack expansion behaviour is subsumed by the “immediately-declared constraint” facility.
A constrained-parameter constraint-expression. The expression is derived from the qualified-concept-nameQ
in the constrained-parameter, its designated conceptC
, and the declared template parameterP
.
First, a template argumentA
is invented fromP
. IfP
declares a template parameter pack ([temp.variadic]) andC
is a variadic concept ([temp.concept]), thenA
is the pack expansionP...
. Otherwise,A
is the id-expressionP
.Then, an id-expressionE
is formed as follows. IfQ
is a concept-name, thenE
isC<A>
. Otherwise,Q
is a partial-concept-id of the formC<A1, A2, ..., An>
, andE
isC<A, A1, A2, ..., An>
.Finally, ifP
declares a template parameter pack andC
is not a variadic concept,E
is adjusted to be the_fold-expression_(E && ...)
(7.5.6).
E
is the introduced constraint-expression.[Example:
template<typename T> concept C1 = true; template<typename... Ts> concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // associates C1<T> template<C1... T> struct s2; // associates (C1<T> && ...) template<C2... T> struct s3; // associates C2<T...>(C2<T> && ...) template<C3<int> T> struct s4; // associates C3<T, int> template<C3<int>... T> struct s5; // associates (C3<T, int> && ...)
—_end example_]
Insert a new paragraph after (old) paragraph 11.
?. A non-type template parameter declared with a type that contains a placeholder type with a type-constraint introduces the immediately-declared constraint of the invented type corresponding to the placeholder.
Delete (old) paragraph 13.
The default template-argument of a _constrained-parameter_shall match the kind (type, non-type, template) of the declared template parameter. [Example: […] —_end example_]
Modify (old) paragraph 19.
If a template-parameter is a type-parameter with an ellipsis prior to its optional identifier or is a parameter-declaration that declares a pack (9.2.3.5), then the template-parameter is a template parameter pack (12.6.3). A template parameter pack that is a parameter-declaration_whose type contains one or more unexpanded packs is a pack expansion. Similarly, a template parameter pack that is a type-parameter with a_template-parameter-list containing one or more unexpanded packs is a pack expansion.A type parameter pack with a type-constraint that contains an unexpanded parameter pack is a pack expansion.A template parameter pack that is a pack expansion shall not expand a template parameter pack declared in the same template-parameter-list.
Modify [temp.constr.decl, 12.4.2] paragraph 2 as follows.
Constraints can also be associated with a declaration through the use of
_constrained-parameter_s_type-constraint_sin a template-parameter-list. Each of these forms introduces additional_constraint-expression_s that are used to constrain the declaration.
Modify paragraph 3 as follows.
A template’s associated constraints are defined as follows:
- […]
- Otherwise, the associated constraints are the normal form of a logical AND expression (7.6.14) whose operands are in the following order:
- the constraint-expression introduced by each
constrained-parametertype-constraint (12.1) in the declaration’s template-parameter-list, in order of appearance, and- the constraint-expression introduced by a requires-clause following a template-parameter-list (Clause12), and
- the constraint-expression introduced by type-constraint in the parameter-type-list of a function declaration
- the constraint-expression introduced by a_trailing requires-clause_ (9.2) of a function declaration (9.2.3.5).
Modify [temp.decls, 12.6] paragraph 2 as follows.
For purposes of name lookup and instantiation, default arguments,
_partial-concept-id_s_type-constraint_s,_requires-clause_s (Clause 12), and _noexcept-specifier_s of function templates and of member functions of class templates are considered definitions; each default argument,_partial-concept-id_stype-constraint,requires-clause, or noexcept-specifier is a separate definition which is unrelated to the templated function definition or to any other default arguments_partial-concept-id_s,_type-constraint_s, _requires-clause_s, or_noexcept-specifier_s. For the purpose of instantiation, the substatements of a constexpr if statement (8.4.1) are considered definitions.
Modify [temp.variadic, 12.6.3] paragraph 5 bullet (5.3.2) as follows.
A pack expansion consists of […]
- […]
- In a template parameter pack that is a pack expansion (12.1):
- if the template parameter pack is a parameter-declaration; the pattern is the parameter-declaration without the ellipsis;
- if the template parameter pack is a type-parameter
with a template-parameter-list; the pattern is the corresponding type-parameter without the ellipsis.- In an initializer-list (9.3); the pattern is an initializer-clause.
- […]
Modify the example in [temp.concept, 12.6.8] paragraph 2 as follows.
... template<C T> // C, as a type-constraint, constrains f2(T) as a constrained-parameter ...
Modify paragraph 6 as follows.
The first declared template parameter of a concept definition is its_prototype parameter_. A type concept is a concept whose prototype parameter is a type template-parameter.
A_variadic concept_ is a concept whose prototype parameter is a template parameter pack.
Modify [temp.res, 12.7] paragraph 8 item (8.2) as follows.
- no valid specialization can be generated for a template or a substatement of a constexpr if statement (8.4.1) within a template and the template is not instantiated, or
- no substitution of template arguments into a
partial-concept-idtype-constraint or requires-clause would result in a valid expression, or- every valid specialization of a variadic template requires an empty template parameter pack, or
- […]
Modify the note in [temp.inst, 12.8.1] paragraph 1 as follows.
[…] [Note: Within a template declaration, a local class (10.5) or enumeration and the members of a local class are never considered to be entities that can be separately instantiated (this includes their default arguments, _noexcept-specifier_s, and non-static data member initializers, if any, but not their
_partial-concept-id_s_type-constraint_sor _requires-clause_s). […]
Modify paragarph 17 as follows.
The
_partial-concept-id_s_type-constraint_sand requires-clause of a template specialization or member function are not instantiated along with the specialization or function itself, even for a member function of a local class; substitution into the atomic constraints […]