Disclaimer
this is not an answer, but rather a long comment
My language-lawyer skills are too low to fully understand the standard but here are a few things I discovered from experimenting with the code. Everything that follows is based on my (far from perfect) understanding of the matter and probably needs some reviews.
Investigation
First off, I went to a fully defined standard version (C++17) so we operate on a well defined implementation.
Looking at this code it seems like MSVC still has some problems (with its lookup, I guess?) when it comes to template instantiation and redefinition. I wouldn't trust MSVC that much in our scenario.
That beeing said, lets think about why we could need the template
keyword at
return yF.template head<1>();
Dependent names
Sometimes, inside templates, we have to help the compiler decide whether a name refers to
- a value
int T::x = 0
,
- a type
struct T::x {};
, or
- a template
template <typename U> T::foo<U>();
If we refer to a value, we do nothing. If we refer to a type, we have to use typename
. And if we refer to a template we use template
. More on that subject can be found here.
I don't understand the standard specification when it comes to the actual definition of a dependent name but here are some observations.
Observations
Lets take a look at the reference code
template <int N>
struct Matrix
{
template <int Idx>
int head() { return Idx; }
};
template <typename T>
struct Test
{
static constexpr int RayDim = 3;
int func() const
{
Matrix<RayDim> yF;
return yF.head<1>(); // clang complains, gcc and msvc are ok
}
};
struct Empty {};
int test()
{
Test<Empty> t;
return t.func();
}
Usually RayDim
should be a dependent name (because it's inside the template Test
) which would cause Matrix<RayDim>
to be a dependent name aswell. For now, lets assume that Matrix<RayDim>
actually is a dependent name. This makes Matrix<RayDim>::head
a dependent name aswell. Since Matrix<RayDim>::head
is a templated function it is a template in itself and the rules of dependent names from above apply, requiring us to use the template
keyword. This is what clang is complaining about.
However, since RayDim
is defined inside Test
and func
is also defined inside the same template and not a templated function in itself, I don't think RayDim
actually is a dependent name in the context of func
. Furthermore, RayDim
doesn't rely on the template arguments of Test
. In this case, Matrix<RayDim>
and Matrix<RayDim>::head
respectively, would become non-dependent names, which allows us to omit the template
keyword. This is why gcc (and msvc) compile.
If we were to template RayDim
aswell, like here
template <typename>
static constexpr int RayDim = 3;
gcc would treat it as a dependent name aswell (which is correct, since there might be a template specialization later so we don't know at that point). Meanwhile, msvc happily accepts everything we throw at it.
Conclusion
It seems like it's boiling down to whether RayDim
is a dependent name in the context of Test<T>::func
or not. Clang thinks it is, gcc doesn't. From some more test it looks like msvc sides with clang on this one. But it's also kinda doing it's own thing, so who knows?
I would side with gcc here as I see no possible way of RayDim
becoming dependent at the point where func
is instantiated.