[Voted into WP at August, 2010 meeting.]
Proposed resolution (February, 2010): Change 13.8.2 [temp.local] paragraph 8 as follows: In the definition of a member of a class template that appears
outside of the class template definition, the name of a member of
this the class template hides the name of a
template-parameter of any enclosing class templates
(but not a template-parameter of the member, if the member
is a class or function template). [Example:
template<class T> struct A {
struct B { /* ... */ };
typedef void C;
void f();
template<class U> void g(U);
};
template<class B> void A<B>::f() {
B b; // A's B, not the template parameter
}
template<class B> template<class C> void A<B>::g(C) {
B b; // A's B, not the template parameter
C c; // the template parameter C, not A's C
}
Notes from the March 2004 meeting: Basically, the standard is okay. We think Gaby's desired
cases like #1 should be ill-formed. There is a wording problem in 13.8.2 [temp.local] paragraph 7.
It says: It should say "hides the name of a template-parameter of the class template
(but not a template-parameter of the member, if the member is itself a
template)" or words to that effect.
In the definition of a member of a class template that appears outside of the
class template definition, the name of a member of this template hides
the name of a template-parameter.
The list of cases in 13.8.2 [temp.local]
about when a template parameter is hidden
seems to be incomplete. Consider Someone asked whether line #1 is well-formed and I responded "no"
based on my understanding of the rules in 14.6.1.
After a second looking, I've realized that the above case is currently
missing from the list. The list in 14.6.1 covers cases like
In the case listed in 14.6.1 (having to do with members of classes),
the "algorithm" seems to be this:
I believe that any rule, coherent with 14.6.1/5 and 14.6.1/7, for
covering the cases of member templates (example-1) will be described
by the above "algorithm". Am I missing something? [1] of course, the standard text does not formally speak of "template
parameter scope", but we all know that the template parameters
"live" somewhere. I'm using that terminology to designate the
declarative region of the template parameters.
Mike Miller:
I have a somewhat different perspective on this question. I
think your example-1 is fundamentally different from your
example-2 and example-3. Looking, for instance, at your
example-2, I see four nested scopes: Naturally, S::C hides the template parameter C. The same is
true of your example-3, with three scopes: Again, it's clear that the C inherited from A hides the template
parameter in the containing scope. The scopes I see in your example-1, however, are different: Here it seems clear to me that the template parameter hides the
class member. It might help to look at the case where the function template is
defined inline in the class: It would be pretty strange, I think, if the #1 C were the member
and not the template parameter. It would also be odd if the
name lookup were different between an inline definition and an
out-of-line definition. See also issue 459.
// example-1
struct S {
int C;
template<class> void f();
};
template<class C>
void S::f()
{
C c; // #1
}
// example-2
template<class T>
struct S {
int C;
void f();
};
template<class C>
void S<C>::f()
{
C c; // ERROR: 'C' is 'S::C' not the template parameter
}
or
// example-3
struct A { int C; }
template<class C>
struct S : A {
C c; // ERROR: 'C' is 'A::C', not the template parameter
};
But the case of a 'member template' is missing. I believe it should
follow the same rule as above. The reason is this.
The above formulation uniformly covers paragraphs 5 and 7 of section
14.6.1 and gives a general view of how name lookup is supposed to
happen.
namespace scope
template scope (where the parameter is)
class S scope
S::f() block scope
namespace scope
template scope
class S scope (includes 10.2 base class lookup)
namespace scope
struct S scope
template scope (where the parameter is)
S::f() block scope
struct S {
int C;
template<class C> int f() {
C c; // #1
}
};