44 KiB
[temp.dep]
13 Templates [temp]
13.8 Name resolution [temp.res]
13.8.3 Dependent names [temp.dep]
13.8.3.1 General [temp.dep.general]
Inside a template, some constructs have semantics which may differ from one instantiation to another.
Such a constructdepends on the template parameters.
In particular, types and expressions may depend on the type and/or value of template parameters (as determined by the template arguments) and this determines the context for name lookup for certain names.
An expression may betype-dependent (that is, its type may depend on a template parameter) orvalue-dependent (that is, its value when evaluated as a constant expression ([expr.const]) may depend on a template parameter) as described below.
A dependent call is an expression, possibly formed as a non-member candidate for an operator ([over.match.oper]), of the form:
postfix-expression ( expression-listopt )
where the postfix-expression is an unqualified-id and
any of the expressions in the expression-list is a pack expansion ([temp.variadic]), or
any of the expressions or braced-init-lists in theexpression-list is type-dependent, or
the unqualified-id is a template-id in which any of the template arguments depends on a template parameter.
The component name of an unqualified-id ([expr.prim.id.unqual]) is dependent if
it is a conversion-function-id whose conversion-type-id is dependent, or
it is operator= and the current class is a templated entity, or
the unqualified-id is the postfix-expression in a dependent call.
[Note 1:
Such names are looked up only at the point of the template instantiation ([temp.point]) in both the context of the template definition and the context of the point of instantiation ([temp.dep.candidate]).
â end note]
[Example 1: template struct X : B {typename T::A* pa; void f(B* pb) {static int i = B::i; pb->j++; }};
The base class nameB, the type nameT::A, the namesB::i andpb->j explicitly depend on thetemplate-parameter.
â end example]
13.8.3.2 Dependent types [temp.dep.type]
A name or template-id refers to thecurrent instantiation if it is
in the definition of a class template, a nested class of a class template, a member of a class template, or a member of a nested class of a class template, the injected-class-name of the class template or nested class,
in the definition of a primary class template or a member of a primary class template, the name of the class template followed by the template argument list of its template-head ([temp.arg]) enclosed in<> (or an equivalent template alias specialization),
in the definition of a nested class of a class template, the name of the nested class referenced as a member of the current instantiation,
in the definition of a class template partial specialization or a member of a class template partial specialization, the name of the class template followed by a template argument list equivalent to that of the partial specialization ([temp.spec.partial]) enclosed in <> (or an equivalent template alias specialization), or
in the definition of a templated function, the name of a local class ([class.local]).
A template argument that is equivalent to a template parameter can be used in place of that template parameter in a reference to the current instantiation.
A template argument is equivalent to a type template parameter if it denotes the same type.
A template argument is equivalent to a constant template parameter if it is an identifier that names a variable that is equivalent to the template parameter.
A variable is equivalent to a template parameter if
it has the same type as the template parameter (ignoring cv-qualification) and
its initializer consists of a single identifier that names the template parameter or, recursively, such a variable.
[Note 1:
Using a parenthesized variable name breaks the equivalence.
â end note]
[Example 1: template class A { A* p1; // A is the current instantiation A* p2; // A is the current instantiation A<T*> p3; // A<T*> is not the current instantiation::A* p4; // ::A is the current instantiationclass B { B* p1; // B is the current instantiation A::B* p2; // A::B is the current instantiationtypename A<T*>::B* p3; // A<T*>::B is not the current instantiation};};
template class A<T*> { A<T*>* p1; // A<T*> is the current instantiation A* p2; // A is not the current instantiation};
template <class T1, class T2, int I> struct B { B<T1, T2, I>* b1; // refers to the current instantiation B<T2, T1, I>* b2; // not the current instantiationtypedef T1 my_T1; static const int my_I = I; static const int my_I2 = I+0; static const int my_I3 = my_I; static const long my_I4 = I; static const int my_I5 = (I); B<my_T1, T2, my_I>* b3; // refers to the current instantiation B<my_T1, T2, my_I2>* b4; // not the current instantiation B<my_T1, T2, my_I3>* b5; // refers to the current instantiation B<my_T1, T2, my_I4>* b6; // not the current instantiation B<my_T1, T2, my_I5>* b7; // not the current instantiation}; â end example]
A dependent base class is a base class that is a dependent type and is not the current instantiation.
[Note 2:
A base class can be the current instantiation in the case of a nested class naming an enclosing class as a base.
[Example 2: template struct A {typedef int M; struct B {typedef void M; struct C; };};
template struct A::B::C : A { M m; // OK, A::M}; â end example]
â end note]
A qualified ([basic.lookup.qual]) or unqualified name is amember of the current instantiation if
its lookup context, if it is a qualified name, is the current instantiation, and
lookup for it finds any member of a class that is the current instantiation
[Example 3: template class A {static const int i = 5; int n1[i]; // i refers to a member of the current instantiationint n2[A::i]; // A::i refers to a member of the current instantiationint n3[A::i]; // A::i refers to a member of the current instantiationint f();};
template int A::f() {return i; // i refers to a member of the current instantiation} â end example]
A qualified or unqualified name names a dependent member of the current instantiation if it is a member of the current instantiation that, when looked up, refers to at least one member declaration (including a using-declarator whose terminal name is dependent) of a class that is the current instantiation.
A qualified name ([basic.lookup.qual]) is dependent if
it is a conversion-function-id whose conversion-type-id is dependent, or
its lookup context is dependent and is not the current instantiation, or
its lookup context is the current instantiation and it is operator=,117 or
its lookup context is the current instantiation and has at least one dependent base class, and qualified name lookup for the name finds nothing ([basic.lookup.qual]).
[Example 4: struct A {using B = int; A f();};struct C : A {};templatevoid g(T t) {decltype(t.A::f())::B i; // error: typename needed to interpret B as a type}template void g(C); // …even though A is ::A here â end example]
If, for a given set of template arguments, a specialization of a template is instantiated that refers to a member of the current instantiation with a qualified name, the name is looked up in the template instantiation context.
If the result of this lookup differs from the result of name lookup in the template definition context, name lookup is ambiguous.
[Example 5: struct A {int m;};
struct B {int m;};
templatestruct C : A, T {int f() { return this->m; } // finds A::m in the template definition contextint g() { return m; } // finds A::m in the template definition context};
template int C::f(); // error: finds both A::m and B::mtemplate int C::g(); // OK, transformation to class member access syntax// does not occur in the template definition context; see [expr.prim.id.general] â end example]
An initializer is dependent if any constituent expression ([intro.execution]) of the initializer is type-dependent.
A placeholder type ([dcl.spec.auto.general]) is dependent if it designates a type deduced from a dependent initializer.
A placeholder for a deduced class type ([dcl.type.class.deduct]) is dependent if
it has a dependent initializer, or
it refers to an alias template that is a member of the current instantiation and whose defining-type-id is dependent after class template argument deduction ([over.match.class.deduct]) and substitution ([temp.alias]).
[Example 6: template<class T, class V>struct S { S(T); };
templatestruct A {template using X = S<T, U>; template using Y = S<T, int>; void f() {new X(1); // dependentnew Y(1); // not dependent}}; â end example]
A type is dependent if it is
a template parameter,
denoted by a dependent (qualified) name,
a nested class or enumeration that is a direct member of a class that is the current instantiation,
a cv-qualified type where the cv-unqualified type is dependent,
a compound type constructed from any dependent type,
an array type whose element type is dependent or whose bound (if any) is value-dependent,
a function type whose parameters include one or more function parameter packs,
a function type whose exception specification is value-dependent,
denoted by a dependent placeholder type,
denoted by a dependent placeholder for a deduced class type,
denoted by a simple-template-id in which either the template name is a template parameter or any of the template arguments is dependent ([temp.dep.temp]),118
denoted by decltype(expression), where expression is type-dependent, or
denoted by a splice-type-specifier in which either the splice-specifier orsplice-specialization-specifier is dependent ([temp.dep.splice]).
[Note 3:
Because typedefs do not introduce new types, but instead simply refer to other types, a name that refers to a typedef that is a member of the current instantiation is dependent only if the type referred to is dependent.
â end note]
Every instantiation of a class template declares a different set of assignment operators.
This includes an injected-class-name ([class.pre]) of a class template used without a template-argument-list.
13.8.3.3 Type-dependent expressions [temp.dep.expr]
Except as described below, an expression is type-dependent if any subexpression is type-dependent.
this is type-dependent if the current class ([expr.prim.this]) is dependent ([temp.dep.type]).
An id-expression is type-dependent if it is a template-id that is not a concept-id and is dependent; or if its terminal name is
associated by name lookup with one or more declarations declared with a dependent type,
associated by name lookup with a constant template parameter declared with a type that contains a placeholder type ([dcl.spec.auto]),
associated by name lookup with a variable declared with a type that contains a placeholder type ([dcl.spec.auto]) where the initializer is type-dependent,
associated by name lookup with one or more declarations of member functions of a class that is the current instantiation declared with a return type that contains a placeholder type,
associated by name lookup with a structured binding declaration ([dcl.struct.bind]) whosebrace-or-equal-initializer is type-dependent,
associated by name lookup with a pack, [Example 1: struct C { };
void g(...); // #1template <typename T>void f() { C arr[1]; auto [...e] = arr;
g(e...); // calls #2}void g(C); // #2int main() { f();} â end example]
associated by name lookup with an entity captured by copy ([expr.prim.lambda.capture]) in a lambda-expression that has an explicit object parameter whose type is dependent ([dcl.fct]),
theidentifierfunc ([dcl.fct.def.general]), where any enclosing function is a template, a member of a class template, or a generic lambda,
associated by name lookup with a result binding ([dcl.contract.res]) of a function whose return type is dependent,
a conversion-function-id that specifies a dependent type,
a name N introduced by the for-range-declaration of an expansion statement S if the type specified for N contains a placeholder type and either
the expansion-initializer of S is type-dependent or
S is not an iterating expansion statement, or
dependent
or if it names a dependent member of the current instantiation that is a static data member of type âarray of unknown bound of Tâ for some T ([temp.static]).
Expressions of the following forms are type-dependent only if the type specified by thetype-id,simple-type-specifier,typename-specifier, ornew-type-id is dependent, even if any subexpression is type-dependent:
simple-type-specifier ( expression-listopt )
simple-type-specifier braced-init-list
typename-specifier ( expression-listopt )
typename-specifier braced-init-list
::opt new new-placementopt new-type-id new-initializeropt
::opt new new-placementopt ( type-id ) new-initializeropt
dynamic_cast < type-id > ( expression )
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
( type-id ) cast-expression
Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):
literal
sizeof unary-expression
sizeof ( type-id )
sizeof ... ( identifier )
alignof ( type-id )
typeid ( expression )
typeid ( type-id )
::opt delete cast-expression
::opt delete [ ] cast-expression
throw assignment-expressionopt
noexcept ( expression )
requires-expression
reflect-expression
[Note 1:
For the standard library macro offsetof, see [support.types].
â end note]
A class member access expression is type-dependent if the terminal name of its id-expression, if any, is dependent or the expression refers to a member of the current instantiation and the type of the referenced member is dependent.
[Note 2:
In an expression of the formx.y orxp->y the type of the expression is usually the type of the membery of the class ofx (or the class pointed to byxp).
However, ifx orxp refers to a dependent type that is not the current instantiation, the type ofy is always dependent.
â end note]
A braced-init-list is type-dependent if any element is type-dependent or is a pack expansion.
A fold-expression is type-dependent.
A pack-index-expression is type-dependent if its id-expression is type-dependent.
A splice-expression is type-dependent if its splice-specifier orsplice-specialization-specifier is dependent ([temp.dep.splice]).
13.8.3.4 Value-dependent expressions [temp.dep.constexpr]
Except as described below, an expression used in a context where a constant expression is required is value-dependent if any subexpression is value-dependent.
Anid-expression is value-dependent if
it is a concept-id and its concept-name is dependent or any of its arguments are dependent ([temp.dep.temp]),
it is type-dependent,
it is the name of a constant template parameter,
it is a name introduced by the for-range-declaration of an expansion statement ([stmt.expand]),
it names a static data member that is a dependent member of the current instantiation and is not initialized in a member-declarator,
it names a static member function that is a dependent member of the current instantiation, or
it names a potentially-constant variable ([expr.const]) that is initialized with an expression that is value-dependent.
Expressions of the following form are value-dependent if theunary-expression or expression is type-dependent or thetype-id is dependent:
sizeof unary-expression
sizeof ( type-id )
typeid ( expression )
typeid ( type-id )
alignof ( type-id )
[Note 1:
For the standard library macro offsetof, see [support.types].
â end note]
Expressions of the following form are value-dependent if either thetype-id,simple-type-specifier, ortypename-specifier is dependent or theexpression orcast-expression is value-dependent or any expression in the expression-list is value-dependent or any assignment-expression in the braced-init-list is value-dependent:
simple-type-specifier ( expression-listopt )
typename-specifier ( expression-listopt )
simple-type-specifier braced-init-list
typename-specifier braced-init-list
static_cast < type-id > ( expression )
const_cast < type-id > ( expression )
reinterpret_cast < type-id > ( expression )
dynamic_cast < type-id > ( expression )
( type-id ) cast-expression
Expressions of the following form are value-dependent:
sizeof ... ( identifier )
fold-expression
unless the identifier is a structured binding pack whose initializer is not dependent.
A noexcept-expression ([expr.unary.noexcept]) is value-dependent if its expression involves a template parameter.
An expression of the form &qualified-id where thequalified-id names a dependent member of the current instantiation is value-dependent.
An expression of the form &cast-expression is also value-dependent if evaluating cast-expression as a core constant expression succeeds and the result of the evaluation refers to a templated entity that is an object with static or thread storage duration or a member function.
A reflect-expression is value-dependent if
it is of the form ^^reflection-name and the reflection-name
is a dependent qualified name,
is a dependent namespace-name,
is the name of a template parameter, or
names a dependent member of the current instantiation ([temp.dep.type]),
it is of the form ^^type-id and the type-id denotes a dependent type, or
it is of the form ^^id-expression and the id-expression is value-dependent.
A splice-expression is value-dependent if its splice-specifier orsplice-specialization-specifier is dependent ([temp.dep.splice]).
13.8.3.5 Dependent splice specifiers [temp.dep.splice]
A splice-specifier is dependent if its converted constant-expression is value-dependent.
A splice-specialization-specifier is dependent if its splice-specifier is dependent or if any of its template arguments are dependent.
A splice-scope-specifier is dependent if its splice-specifier orsplice-specialization-specifier is dependent.
[Example 1: template<auto T, auto NS>void fn() {using a = [:T:]<1>; // [:T:]<1> is dependent because [:T:] is dependentstatic_assert([:NS:]::template TCls<1>::v == a::v); // [:NS:] is dependent}namespace N {template struct TCls { static constexpr int v = V; };}int main() { fn<^^N::TCls, ^^N>();} â end example]
[Example 2: template<template class X>struct S {[:^^X:]<int, float> m;};
template struct V1 {};template<class, class = int> struct V2 {};
S s1; // error: V1<int, float> has too many template arguments S s2; // OK â end example]
13.8.3.6 Dependent namespaces [temp.dep.namespace]
A namespace alias is dependent if it is introduced by a namespace-alias-definition whose qualified-namespace-specifier (if any) is a dependent qualified name or whose splice-specifier (if any) is dependent.
A namespace-name is dependent if it names a dependent namespace alias.
[Example 1: template<std::meta::info R>int fn() {namespace Alias = [:R:]; // [:R:] is dependentreturn typename Alias::T{}; // Alias is dependent}namespace NS {using T = int;}int a = fn<^^NS>(); â end example]
13.8.3.7 Dependent template arguments [temp.dep.temp]
A typetemplate-argument is dependent if the type it specifies is dependent.
A constanttemplate-argument is dependent if its type is dependent or the constant expression it specifies is value-dependent.
Furthermore, a constanttemplate-argument is dependent if the corresponding constant template parameter is of reference or pointer type and the template-argument designates or points to a member of the current instantiation or a member of a dependent type.
A template argument is also dependent if it is a pack expansion.
A template template parameter is dependent if it names a template parameter or its terminal name is dependent.