I encountered the following problem by compiling the following example:
template <int N>
class Matrix {
public:
template <int Idx>
int head() {
return Idx;
}
};
template <typename T>
class Test {
static constexpr int RayDim = 3;
public:
int func() const {
Matrix<RayDim> yF;
return yF.head<1>();
// ^ is template keyword required here?
}
};
struct Empty {};
void test() {
Test<Empty> t;
}
Link to Compiler Explorer: https://godbolt.org/z/js4XaP
The code compiles with GCC 9.2 and MSVC 19.22 but not with clang 9.0.0. Clang states that a template keyword is required. If static constexpr int RayDim = 3;
is moved into int func() const
clang accepts it.
As stated as a comment in the code block, is the template keyword required for yF.head<1>()
?
A dependent name is essentially a name that depends on a template parameter. A dependent name can be a type, a non-type, or a template parameter. To express that a dependent name stands for a type or a template, you have to use the keywords typename or template .
A dependent name is a name that depends on the type or the value of a template parameter. For example: template<class T> class U : A<T> { typename T::B x; void f(A<T>& y) { *y++; } }; The dependent names in this example are the base class A<T> , the type name T::B , and the variable y .
Technically (and truly), C++ has dependent types, as types can depend on values ... | Hacker News.
" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.
The template
keyword should not be required here, so clang is incorrect to reject the program.
All C++ Standard section and paragraph numbers and quotes below are the same for C++17 draft N4659 and the current linked C++20 draft.
The requirement for template
after a .
or ->
or ::
token when naming a member template is in [temp.names]/4. The paragraph first lists cases where the keyword is not allowed, then cases where it is optional and doesn't make a difference, then:
In all other contexts, when naming a template specialization of a member of an unknown specialization ([temp.dep.type]), the member template name shall be prefixed by the keyword
template
.
A "member of an unknown specialization" is a member of a dependent type which is not "the current instantiation". So the question is whether Matrix<RayDim>
is a dependent type. For that, we look at [temp.dep.type]/9:
A type is dependent if it is
- a template parameter,
- a member of an unknown specialization,
- a nested class or enumeration that is a dependent member of 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 exception specification is value-dependent,
- a simple-template-id in which either the template name is a template parameter or any of the template arguments is a dependent type or an expression that is type-dependent or value-dependent or is a pack expansion, or
- denoted by
decltype(
expression)
, where expression is type-dependent.
Matrix<RayDim>
is clearly not a template parameter, any kind of member, cv-qualified, an array type, a function type, or specified by decltype
. It is a compound type, but it uses only a template-name and an expression, so is not constructed from any other type.
That leaves the simple-template-id case. The template name Matrix
is not a template parameter. The template argument RayDim
is an expression, so now to see if it is type-dependent or value-dependent.
"Type-dependent" is defined in [temp.dep.expr]. Only paragraph 3 can apply to a lone identifier like RayDim
:
An id-expression is type-dependent if it contains
- an identifier associated by name lookup with one or more declarations declared with a dependent type,
- an identifier associated by name lookup with a non-type template-parameter declared with a type that contains a placeholder type,
- an identifier 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,
- an identifier associated by name lookup with one or more declarations of member functions of the current instantiation declared with a return type that contains a placeholder type,
- an identifier associated by name lookup with a structured binding declaration whose brace-or-equal-initializer is type-dependent,
- the identifier
__func__
([dcl.fct.def.general]), where any enclosing function is a template, a member of a class template, or a generic lambda,- a template-id that is dependent,
- a conversion-function-id that specifies a dependent type, or
- a nested-name-specifier or a qualified-id that names a member of an unknown specialization;
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 someT
([temp.static]).
RayDim
certainly does not contain any __func__
, template-id, conversion-function-id, nested-name-specifier, or qualified-id. Name lookup finds the class template's static member declaration. That declaration of RayDim
is certainly not a template-parameter, a member function, or a structured binding declaration, and its type const int
is certainly not a dependent type or array type and does not contain a placeholder type. So RayDim
is not type-dependent.
"Value-dependent" is defined in [temp.dep.constexpr]. The only cases that can apply to a lone identifier like RayDim
are in paragraph 2:
An id-expression is value-dependent if:
- it is type-dependent,
- it is the name of a non-type template parameter,
- 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 is a constant with literal type and is initialized with an expression that is value-dependent.
From above, RayDim
is not type-dependent. It is certainly not a template parameter or a member function. It is a static data member and dependent member of the current instantiation, but it is initialized in the member-declarator. That is, the "= 3
" appears inside the class definition, not in a separate member definition. It is a constant with literal type, but its initializer 3
is not value-dependent.
So RayDim
is not value-dependent or type-dependent. Therefore Matrix<RayDim>
is not a dependent type, yF.head
is not a member of an unknown instantiation, and the template
keyword before head
is optional, not required. (It is permitted since it's not in a "type-only context" and head
does in fact name a member template.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With