Boost logo

Boost :

From: Gabriel Dos Reis (gdr_at_[hidden])
Date: 2004-02-14 12:00:58


"Giovanni Bajo" <giovannibajo_at_[hidden]> writes:

| David Abrahams wrote:
|
| >> struct A {
| >> typedef int M;
| >>
| >> template <class M>
| >> void foo(void) {
| >> M m; // which M is this?
| >> }
| >> };
| >>
| >> I know the C++ committe is discussing this issue at this moment. The
| >> argument would be that "M" names the typedef because it's "more
| >> stable" than the template parameter (which could get renamed in an
| >> out-of-class definition). See also http://gcc.gnu.org/PR13967 for a
| >> detailed discussion.
| >
| > I'm sorry, but that's insane from a usability POV. C++ already has

Dave, I got the copy of the message you sent. While the rules as
currently defined are not clear and not even what you would expect, I
don't agree that "insane" (or such words) are qualifications that
should be used if you wanted a technical discussion about your request.

| > too many places where something far away can be chosen instead of the
| > "obvious" alternative close by (see ADL).

When it comes to name look up, I think "obvious" does not carry any
meaning that would be of help -- I'm not just speaking of what C++
current rules say about name look-up.

| I'm not advocating that, I'm just saying that it's how GCC currently
| works and it seems to be a gray area of the standard. My personal
| opinion is that GCC is wrong: I agree with you that the template
| parameter should be found on name lookup.

And it should be also pointed out that GCC is not the only compiler
that interprets the construct differently from what you would consider
"obvious". And the problem has to do with the way the current
rules are worded (the least that can be said is that the rules are
incomplete). We have had a long discussion about this on the CWG
reflector. This undoubtely is going to be debated again at the next
meeting.

One thing that should be kept in the fore is that, when you provide an
in-class definition of a member function, that definition gets
re-evaluated as an out-of-class definition; that is not a requirement
but, it is a common strategy in order to take into account use of
names that are declared "later" in the class. If the re-evaluation
of the definition in the completed scope differs from the evaluation
of the in-class definition, then the program is ill-formed and no
diagnostic is required. Some compilers however, as a QoI, issues
diagnostics on the cases they can catch. For example,

     typedef int A;
     struct B {
       A x;
      void f();
      struct A { };
     };

is ill-formed and it is likely that your favorite compiler will complain.

Now, what is the relation with the member template case presented above?
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 I already disagree with.

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 and in the "C"
in the body of #1 will not be the "obvious" close by
(template-parameter). During the discussion on the CWG reflector, we
introduced the notion of "valid template-parameter renaming"
(which is similar to what FP community calls valid "alpha conversion").
I proposed that if an out-of-class definition of a member template uses
an invalid template-parameter renaming, then the program is
ill-formed. The idea is to avoid misunderstanding about the `"obvious"
alternative close by' between programmer and compiler.

| > Introducing a typedef in an
| > enclosing namespace should not affect the meaning or well-formedness
| > of a use of a template parameter, especially because this sort of
| > thing is liable to happen due to changes in #includes.

Dave, I can't find anything like the one you're describing in the
example you gave. Class-scope declarations are "preserved" against
outside perturbation -- that is what the re-evaluation rules is about.

-- Gaby


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk