Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2002-12-01 16:11:15


Beman Dawes <bdawes_at_[hidden]> writes:

> At 08:11 PM 11/28/2002, David Abrahams wrote:
>
> >> AFAIK, Metrowerks is correct, and the other compilers intend to detect
> >these errors in the future.
> >>
> >> The fix is usually to add qualification. For example,
> >foo::out_of_range_bit.
> >
> >The fix is usually to make sure that the appropriate declaration of a
> >non-dependent name is visible before the name is used. Extra
> >qualification doesn't usually help AFAICT.
> >
> >Oh, I guess that if the name is a member of a base class, then the fix
> >is to write ``this->member_name'' or ``BaseClass::member_name''
> >instead of just ``member_name''. That can be viewed as adding
> >qualification.
>
> Yes, that was the case that came up in other libraries.
>
> > However, that doesn't _appear_ to be the problem in the
> >case below, and in any case I recomment using ``this->'' when possible
> >because it's more reliable.
>
> Hum... I'll take your word for it, but to tell the truth I have
> trouble understanding what either ``this->member_name'' or
> `BaseClass::member_name'' adds. And the format library case wasn't a
> member name at all, it was an enum name, IIRC.

Which is why (unless the enum was a member of a dependent base class)
adding qualification is probably not the right fix in this case.

> I guess I need a C++ for dummies level description of why these
> names are not visible, and why adding
> this-> or qualification makes the names visible.

It's pretty simple:

0. A "dependent" name is one whose meaning depends on the type or
   value of a template parameter.

1. templates are parsed in phase 1 and all non-dependent names are
   bound at that point. Any unqualified names which are not followed
   by dependent arguments (e.g. some_name(x), where x is of type T)
   are considered to be non-dependent.

1.5 It is in phase 1 that the compiler also decides whether each name
   is referring to a member or a non-member. I guess the theory is
   that we don't want our templates to suddenly start using members
   instead of non-members because we instantiated them with a base
   class that has an unfortunate member name:

       template <class T> void f(T x);

       template <class B> struct D : B
       {
            D() { typedef typename B::some_type some_type; f(some_type()); }
       };

       struct Base
       {
            f(int);
            typedef int some_type;
       };

       D<Base> x; // uh-oh?

   In this case, f is dependent, because ADL says to look it up based
   on argument types. However, phase 1 parsing determines that it's a
   non-member name because it's not defined in D or any of its
   non-dependent bases (of which it has none).

2. When templates are instantiated, phase 2 lookup goes into effect
   and dependent names are bound.

3. If a base class is dependent on (or is) a template parameter, you
   can't know the base's exact definition until all specializations
   have been seen, so unqualified names never refer to members of
   dependent bases. They are always treated as non-dependent and bound
   in phase 1 at the template's point-of-definition. By the time we
   get to phase 2 and know the definition of the actual base, it's too
   late.

4. If the template has dependent bases, and the name is not found in
   the template itself, you can make the name dependent by adding
   this->, since then its definition may depend on the base class.

So, adding this-> or BaseClass:: doesn't actually make the names
visible. Instead it

   a. Marks the name as a member in phase 1

   b. delays lookup of the names until phase 2 when they might be
   visible.

HTH,
Dave

-- 
                       David Abrahams
   dave_at_[hidden] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

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