Topic: using a typedef to bring names into the scope of a derived class - legal or illegal?


Author: Christopher Eltschka <celtschk@web.de>
Date: 26 Jun 01 08:16:32 GMT
Raw View
kavdeiv@mail.ru (Kiril Avdeiv) writes:

> I stumbled repeatedly upon the same technique in a lot of versions of
> the STL (free and commercial alike) and I seriously doubt its
> legitimacy. Below is an explanation.
>
> If the name of a base class of a class template depends on a template
> parameter, the base class name is not found by unqualified lookup both
> at the point of definition of the class template and at the point of
> its instantiation.
>
> To alleviate that the implementers use the following approach (the
> following snippet is taken from SGI STL):
>
> // Base class for ordinary allocators.
> template <class _Tp, class _Alloc, bool __is_static>
> class _Deque_alloc_base {
> public:
>   typedef typename _Alloc_traits<_Tp,_Alloc>::allocator_type
>     allocator_type;
> ...
> };
>
>
> template <class _Tp, class _Alloc>
> class _Deque_base
>   : public _Deque_alloc_base<_Tp,_Alloc,
>                               _Alloc_traits<_Tp,
> _Alloc>::_S_instanceless>
> {
> public:
> ...
>   typedef typename _Base::allocator_type allocator_type;  // <- !!
> ...
> };
>
> I doubt the validity of the above construct, which supposedly brings
> the name "allocator_type" from the scope of the base class into the
> scope of the derived class. 7.1.3/2 says that a typedef can be used to
> redefine the name of any type declared in the same scope to refer to
> the type to which it already refers. In no way does it speak about
> creating synonyms for types declared in a different scope! On the
> other hand a using-declaration is meant to do exactly that - introduce
> a name from one scope into another.
>
> So, my question is - Shouldn't the above declaration been written as
>   using typename _Base::allocator_type;
> instead of
>   typedef typename _Base::allocator_type allocator_type;
> ?
> Or are both the forms equally valid C++?

I'm not sure if "using" would be valid in this case.

"typedef" is definitiely valid.

Look at the following example:

class Base
{
public:
  typedef int X;
};

class Derived: public Base
{
public:
  typedef Base::X Y;
};

This code is obviously correct. Now let's change it a little bit:

class Base
{
public:
  typedef int X;
  typedef double Y;
};

class Derived: public Base
{
public:
  typedef Base::X Y;
};

This code is still correct, because Derived has it's own scope
different from Base; Derived::Y is therefore independant from Base::Y
and hides it. Derived::Y is typedef'd to Base::X, that is, to int,
while Base::Y is double.

Now, let's change the example again:

class Base
{
public:
  typedef int X;
  typedef int Y;
};

class Derived: public Base
{
public:
  typedef Base::Y Y;
};

Now, this is essentially the same as the previous, except that Base::Y
now has the same type as Base::X (and therefore the same type as
Derived::Y). But there's no rule that the hidden name must have a
different definition from the non-hidden name.

Now, let's do the final step:

class Base
{
public:
  typedef int Y;
};

class Derived: public Base
{
public:
  typedef Base::Y Y;
};

Now, this is basically the same as the previous. Derived::Y is defined
to be the same type as Base::Y, and at the same time it hides
Base::Y. That the type typedef'd to has the same name is no problem,
of course, and that there is an entity of the same name in Base is no
problem either, but just covered by name hiding.

Note that it is _not_ a re-definition of Base::Y, since it is in a
completely different scope. Derived::Y is something completely
different from Base::Y, except that it described the same type
(because it's typedef'd to do so) and at the same time hides it
(because it just happens to have the same name).

Now, the templates don't change this, except that Base<T>::Y isn't
found in Derived<T> even if not hidden, unless explicitly qualified.

The key point is that the Base scope is _not_ part of the Derived
scope. It's just searched in (just as global scope is searched if a
name isn't found in local scope).

      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]




Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: 30 May 01 12:53:48 GMT
Raw View
"Kiril Avdeiv" <kavdeiv@mail.ru> wrote in message
news:873f7ae8.0105241311.7f9653ef@posting.google.com...
> I stumbled repeatedly upon the same technique in a lot of versions of
> the STL (free and commercial alike) and I seriously doubt its
> legitimacy. Below is an explanation.
>
> If the name of a base class of a class template depends on a template
> parameter, the base class name is not found by unqualified lookup both
> at the point of definition of the class template and at the point of
> its instantiation.
>
> To alleviate that the implementers use the following approach (the
> following snippet is taken from SGI STL):
>
> // Base class for ordinary allocators.
> template <class _Tp, class _Alloc, bool __is_static>
> class _Deque_alloc_base {
> public:
>   typedef typename _Alloc_traits<_Tp,_Alloc>::allocator_type
>     allocator_type;
> ...
> };
>
>
> template <class _Tp, class _Alloc>
> class _Deque_base
>   : public _Deque_alloc_base<_Tp,_Alloc,
>                               _Alloc_traits<_Tp,
> _Alloc>::_S_instanceless>
> {
> public:
> ...
>   typedef typename _Base::allocator_type allocator_type;  // <- !!
> ...
> };
>
> I doubt the validity of the above construct, which supposedly brings
> the name "allocator_type" from the scope of the base class into the
> scope of the derived class. 7.1.3/2 says that a typedef can be used to
> redefine the name of any type declared in the same scope to refer to
> the type to which it already refers. In no way does it speak about
> creating synonyms for types declared in a different scope! On the
> other hand a using-declaration is meant to do exactly that - introduce
> a name from one scope into another.
>
> So, my question is - Shouldn't the above declaration been written as
>   using typename _Base::allocator_type;
> instead of
>   typedef typename _Base::allocator_type allocator_type;
> ?
> Or are both the forms equally valid C++?
>

typedef creates a synonym for a type. The most common use of this is to
create a synonym in the current scope for a type in another scope, or for a
particular instantiation of template class.

The point that 7.1.3/2 makes is that

typedef I I;

is OK for any type I.

Certainly, using declarations can also be used for the example case you
quote.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optoelectronics
The opinions expressed in this message are not necessarily those of my
employer




      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]

[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]




Author: kavdeiv@mail.ru (Kiril Avdeiv)
Date: 25 May 2001 08:00:12 -0400
Raw View
I stumbled repeatedly upon the same technique in a lot of versions of
the STL (free and commercial alike) and I seriously doubt its
legitimacy. Below is an explanation.

If the name of a base class of a class template depends on a template
parameter, the base class name is not found by unqualified lookup both
at the point of definition of the class template and at the point of
its instantiation.

To alleviate that the implementers use the following approach (the
following snippet is taken from SGI STL):

// Base class for ordinary allocators.
template <class _Tp, class _Alloc, bool __is_static>
class _Deque_alloc_base {
public:
  typedef typename _Alloc_traits<_Tp,_Alloc>::allocator_type
    allocator_type;
...
};


template <class _Tp, class _Alloc>
class _Deque_base
  : public _Deque_alloc_base<_Tp,_Alloc,
                              _Alloc_traits<_Tp,
_Alloc>::_S_instanceless>
{
public:
...
  typedef typename _Base::allocator_type allocator_type;  // <- !!
...
};

I doubt the validity of the above construct, which supposedly brings
the name "allocator_type" from the scope of the base class into the
scope of the derived class. 7.1.3/2 says that a typedef can be used to
redefine the name of any type declared in the same scope to refer to
the type to which it already refers. In no way does it speak about
creating synonyms for types declared in a different scope! On the
other hand a using-declaration is meant to do exactly that - introduce
a name from one scope into another.

So, my question is - Shouldn't the above declaration been written as
  using typename _Base::allocator_type;
instead of
  typedef typename _Base::allocator_type allocator_type;
?
Or are both the forms equally valid C++?

Thanks in advance
Kiril
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]