Tuesday, 20 August 2013

Can typename be omitted in the type-specifier of an out of line member definition?

Can typename be omitted in the type-specifier of an out of line member
definition?

I ran into this strange behaviour when testing whether or not typename is
required by clang. Both clang and gcc accept this code while msvc rejects
it.
template<class T1>
struct A
{
template<class T2>
struct B
{
static B f;
static typename A<T2>::template B<T1> g;
};
};
template<class T1>
template<class T2>
typename A<T2>::template B<T1> // ok, typename/template required
A<T1>::B<T2>::g;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang/gcc accept, msvc rejects missing typename
A<T1>::B<T2>::f;
In general, a qualified-id C<T>::M<U> (where C<T> is a dependent name)
should be written typename C<T>::template M<U>. Is the behaviour of
gcc/clang incorrect, or is there an exception to the general rule (quoted
below) in this particular case?
It could be argued that C<T> is not a dependent name, or that M<U> refers
to a member of the current instantiation. However, at the point of parsing
the type-specifier it's not possible to know that the current
instantiation is C<T>. It seems unfair to require the implementation to
guess that C<T> is the current instantiation.
14.6 Name resolution [temp.res]
A name used in a template declaration or definition and that is dependent
on a template-parameter is assumed not to name a type unless the
applicable name lookup finds a type name or the name is qualified by the
keyword typename.
14.2 Names of template specializations [temp.names]
When the name of a member template specialization appears after . or -> in
a postfix-expression or after a nested-name-specifier in a qualified-id,
and the object or pointer expression of the postfix-expression or the
nested-name-specifier in the qualified-id depends on a template parameter
(14.6.2) but does not refer to a member of the current instantiation
(14.6.2.1), the member template name must be prefixed by the keyword
template. Otherwise the name is assumed to name a non-template.
To further investigate what clang is doing here, I also tried this:
template<class T1>
struct C
{
template<class T2>
struct D
{
static typename A<T1>::template B<T2> f;
static typename A<T1>::template B<T2> g;
};
};
template<class T1>
template<class T2>
typename A<T1>::template B<T2> // ok, typename/template required
C<T1>::D<T2>::f;
template<class T1>
template<class T2>
A<T1>::B<T2> // clang rejects with incorrect error
C<T1>::D<T2>::g;
Clang gives error: redefinition of 'g' with a different type, but the type
of g actually matches the declaration.
I would instead expect to see a diagnostic suggesting the use of typename
or template.
This gives credit to the hypothesis that clang's behaviour in the first
example is unintended.

No comments:

Post a Comment