|
Boost : |
From: Gabriel Dos Reis (gdr_at_[hidden])
Date: 2004-02-14 14:37:24
David Abrahams <dave_at_[hidden]> 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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk