
David Abrahams <dave@boost-consulting.com> writes: [...] | > When you have something like | > | > struct A { | > typedef int M; | > | > template <class M> | > void foo(void) { | > M m; // which M is this? | > } | > }; | > | > it gets evaluated as if | > | > struct A { | > typedef int M; | > template<class M> void f(); | > }; | > | > template<class M> | > void A::f() | > { | > M m; | > } | > | > | > The name look-up rules as currently defined do not attach the template | > parameters to the lexical scope of the member template, so when in the | > body of A::f(), the scope stack looks like this: | > | > namespace scope (global scope) | > template scope (contains class M) | > class A scope (contains typedef int M;) | > A::f()-body scope | > | > Therefore, look-up for M finds the class-scope declaration "typedef | > int M;" -- which is the `"obvious" alternative close by' if I were to | > use your words | | Not AFAICS. The template parameter is closer to the use of M and to | my eye, much more obvious. Given the above scope stack, the use of M is closer to "typedef int M;" than it is to "class M". And the whole point is that "close to eye" and "much more obvious" does not translate well. As I said earlier, to make the defintion "works `obviously'", one needs to resuffle the scope stack; that resuffling does not follow from the out-of-class definition syntax -- and any thing like behind the scene that does not correspond to syntax is "non-obvious" to some people. So assertions with "close to eye" or "much more obvious" are not good guides ;-) | > In order to choose the other `"obvious" alternative close by', the | > out-of-class definition for member template syntax should have been | > | > A:: // <-- Note the enclosing A | > template<class M> | > void f() | > { | > M m; | > } | > | > which would yield the scope stack | > | > namespace scope (global scope) | > class A scope (contains typedef int M;) | > template scope (contains class M) | > A::f()-body scope | > | > The point of the discussion we reached last week (with no vote to evaluate | > consensus) was that, we would change/clarify the lookup rules so that | > when the compiler sees the out-of-class definition of a | > member-template it does a resuffling of the scope stack to make it | > look like what one would get did we have the "right" syntax (as shown above). | > | > The case posted by David Vandevoorde on the newsgroups is not revolved | > by the change I mentioned above. It should also be pointed out the | > decision of have a member name hides an enclosing template-parameter | > name was a conscious decision, made after debate. | > | > Now, just to illustrate the point "name look up" is not an "obvious" | > thing; let's condider the following example | > | > template<class T> | > struct S { | > typedef char C; | > template<class> | > int f(); | > }; | > | > Consider the following two "possible" out-of-class definitions | > | > // alternative #1 | > | > template<class C> | > template<class U> | > int S<C>::f() | > { return sizeof (C); } | > | > // alternative #2 | > template<class T> | > template<class C> | > int S<T>::f() | > { return sizeof (C); } | > | > | > As interpreted by at least 3 or 4 major compilers, the "C" in the body | > consistently refers to S<>::C. If changed abruptly to the "obvious" | > close by, then the two definitions are not equivalent | | Right. | | > and in the "C" in the body of #1 will not be the "obvious" close by | > (template-parameter). | | Why not? (1) Are you asking why it does not refer to the template-parameter? Because the template scope is entered first, followed by the class-scope of S<>, and that class-scope contains a declaration that hides the template-parameter. (2) Are you asking the non-obviousness? If "obvious" or "close to eye" is defined in terms of what eye literaly sees, then the #1 is not obvious because the "C" in the definition (template-parameter) is not the "C" that would be selected by name look-up. | It seems to me you could design the rules so the "C" in | each case referred to a (different) template parameter. One can design anything one can think of :-) The issue is whether we want things follow from simple, first principles. Anything that appeals for special cases is a potential source of confusion (ADL is a good example). If you design the rules as a long list of special cases, then it is likely that you'll end up with something very complex, irregular and probably miss some cases (and we're missign cases in the current C++ rules) If you follow the first principle that scopes nest, then the "C" in the altenrative #1 gets hidden by the member S<>::C in the local scope of S<>::f()-body (current situation). If you want something different, you'll end up with a notion similar to "parallel scopes" or "injection of enclosing scope into the enclosed scopes" (which is contrary to "scopes nest"). -- Gaby