My first code fragment compiles and works fine:
template <class x> struct B1
{
template <class x> struct B2 { int getX() { return(16); } };
template <class x> struct B2<x*> { int getX() { return(20); } };
};
void main(int argc, char* argv[])
{
B1<int>::B2<int> a1;
B1<int>::B2<int*> a2;
printf("a1=%d, a2=%d.\r\n", a1.getX(), a2.getX());
}
Note that the name of template param is x in both templates and this is not confusing the compiler. The second example fails with the compiler crash (MSVC 2008), i.e. without giving any syntax error:
template <class x> struct B1
{
template <class x> struct B2 { int getX() { return(16); } };
template <class x> struct B2<x*>;
};
template <class x> template <class x>
struct B1<x>::B2<x*>
{
int getX() { return(3); }
};
My question is not on how to fix this example, this is obvious.
I looked in the standard (C++2003) on the rules of accessing params of template headers and I cannot find anything relevant. Maybe I am missing something.
While processing B1<x>, should compiler consider only params from the first template header? More likely yes. Then while processing B2<x>, should it consider both? More complex case is especially interesting when params of B1/B2 contain qualified identifiers themselves and these identifiers want to access params of the template header. For simplicity I am not giving the full example for this.
If anybody came across this and can comment or know some artickles/books, YouTube videos, etc, I would love to hear this.
UPDATE: I just tried the following with MSVC:
template <class x> struct B1
{
x qq1;
struct B2
{
int x;
};
};
This compiles and works as expected. I also tried an exe that accesses the inner data field x. This shows that MS compiler implemented hiding template params in the inner scopes. For me this is logical even if this does not comply with the standard.
Although the question has been considered answered by the comments, I’ll provide some further details below. Note that my answer is based on C++11 (ISO/IEC 14882-2011) only.
Part 1: Can a template parameter be re-used as template-parameter of a nested member template?
There are two key statements in the standard about the status of template parameters (specifically, type-parameters – which are the only kind relevant to the question). The first statement describes them as having the same status as typedef-names:
And regarding the possible redeclaration of typedef-names, we have:
(Remark: The rule above seems to apply to the use of typedef specifiers only, although alias declarations (§7.1.3/2) and template parameter declarations (§14.1/3) can be used to declare typedef-names as well. That is, the rule above does not explicitly rule out the use of an alias declaration or indeed a template parameter to redeclare a typedef-name within the same scope, although that is clearly the intended meaning. The wording should be “no typedef-name declaration shall be used” instead of “a typedef specifier shall not be used”.)
What it means is that you cannot do this:
because the second declaration occurs in the same scope in which
Twas originally declared. However, this:is perfectly legally according to the rule above, because the second declaration is in a block scope which (although the first
Tis still valid there) is not the scope in whichTwas originally declared.Due to §14.1/3 quoted above, we must assume that the rule applies to template parameter declarations as well, hence something like
is illegal even on the simple basis that it implies the declaration of the same typedef-name twice within the same scope.
However, in a case like
one may argue that the second declaration of
template <typename X>applies to a nested scope. Fortunately, the standard does provide the following second statement about the status of template parameters:As clearly stated, the no-redeclaration rule that applies to any typedef-name within the declaration scope, applies to nested scopes as well in the case of template parameters.
Here is an example to motivate why I think that rule is actually useful, too. Consider:
The three functions of the inner template all refer to the same static member of the outer class, but in three different ways.
id2()andid3()do this because §14.6.2.1/4 requiresOuter::outerIDandouterIDto be interpreted as referring to the current instantiation, which isOuter<T1>::outerID.If we now replace the template parameter of the inner template with
T1, the same as for the outer template, the meaning ofid1()would change (becauseOuter<T1>would now refer to whatever the definition ofT1in the inner template is), butid2()andid3()would – most naturally – still refer to the current instantiation of the templateouterIDbelongs to. Therefore,id1()may return a different value thanid2()andid3(), which would be most awkward.Part 2: In a partial specialization of a member template, can template parameters of the member be used as template arguments of the enclosing class, and vice versa?
Another problem addressed by the question is whether in a specialization or outside-of-class definition of a member template, such as
the template argument list of the outer template (i.e.
<A>in this case) could make use of parameters defined for the inner template (i.e.Bin this case), and vice versa.Let’s first consider the special case given in the question, where the two parameters are identical:
Although we have ruled this out in Part 1 already due to the re-declaration problem, we can still consider the intended meaning of this syntax, which is: Define an explicit specialization of the inner class template, in which the template argument is assumed to be same as for the outer template. The syntactically correct way to write that is
i.e. the second parameter list would be empty. Unfortunately, this would amount to an explicit specialization of a member template, which is illegal unless the enclosing template is also explicitly specialised (§14.7.3/16).
However, if we consider a partial, rather than an explicit specialization of a member template with two or more parameters, the declaration becomes legal:
We have now used the template argument
Aof the enclosing template as specialised second argument for the inner template as well. Any instantiation of the template that uses the same data type for the template argument of the outer class as well as the second template argument of the inner class, e.g.will instantiate the specialization defined above.
Hence, using template parameters of an enclosing class in the template argument list of a member template is without problems, and the reason is that by the time
Inner<B,A>is evaluated,Aalready has the status of a typedef-name defined at the scope level ofOuter.But doing it reversely, e.g.
will not work, because
Bis a typedef-name of theInnerscope only. The Standard states:I interpret this to mean that the two template parameter lists (one followed by the other) are kept separately, not considered as one combined list of template parameters. Hence in
the interpretation of
Outer<B>cannot make use of the second template parameter list, andBwill be undefined, whereas in the previous casethe interpretation of
Inner<B,A>is possible becauseAhas the status of a typedef-name declared in a scope thatInneris part of, i.e. it will be successfully interpreted asInner<B,Outer::A>, which in turn is the same asInner<B,Outer<A>::A>.