In the following example, A
has a member typedef Instantiate
which causes the instantiation of B<A>
.
template<typename T>
struct B
{
typedef typename T::Before Before; // ok
typedef typename T::After After; // error: no type named 'After' in 'A<int>'
};
template<typename T>
struct A
{
typedef int Before;
typedef typename B<A>::After Instantiate;
typedef int After;
};
template struct A<int>; // instantiate A<int>
All the compilers I've tried report that, while A::Before
is visible, A::After
is not. Is this behaviour compliant with the standard? If so, where does the standard specify which names in A
should be visible during instantiation of B<A>
?
If dependent names are "looked up at the point of the template instantiation", what does this mean in the scenario of a name qualified by a template parameter such as T::After
?
EDIT: Note that the same behaviour occurs when A is not a template:
template<typename T>
struct B
{
typedef typename T::Before Before; // ok
typedef typename T::After After; // error: no type named 'After' in 'A'
};
struct A
{
typedef int Before;
typedef B<A>::After Instantiate;
typedef int After;
};
.. and G++ accepts the following, but Clang does not:
template<typename T>
struct B
{
static const int value = 0;
static const int i = T::value; // clang error: not a constant expression
};
struct A
{
static const int value = B<A>::value;
};
EDIT: After some reading of the C++03 standard:
[temp.dep.type] A type is dependent if it is a template parameter
Therefore T
is dependent.
[temp.res] When looking for the declaration of a name used in a template definition, the usual lookup rules are used for nondependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known.
The lookup of T::After
is therefore postponed until the argument for T
is known.
[temp.inst] Unless a class template specialization has been explicitly instantiated ... the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type.
Therefore the declaration of A<int>::Instantiate
requires the instantiation of B<A>
(because it is used in a nested-name-specifier.)
A<int>::After
is not visible at the point of declaration of A<int>::Instantiate
, so the behaviour of the compiler makes sense - but I haven't seen anything in C++03 that explicitly describes this behaviour. The closest thing was this somewhat vague paragraph:
[temp.dep.res] In resolving dependent names, names from the following sources are considered:
— Declarations that are visible at the point of definition of the template.
A
is a template. I'm still a bit confused about [temp.point]/4. Better leave it with the template, the answer might be different. I might as well change my example – BakehouseT
and this name has already been declared (as a template parameter) when used to define e.g.B<A>::before
. It doesn't care for name lookup thatT
refers toA
, as far as I understand it. – Bakehouse