Topic: Templatised typedefs


Author: "Sam Lindley" <sam@redsnapper.net>
Date: 1999/08/04
Raw View
> >template<class Ch, class Tr = char_traits<Ch> >
> >class A {
> >public:
> >    typedef basic_string<Ch, Tr> Str;
> >    typedef basic_istream<Ch, Tr> Ist;
> >    typedef basic_ostream<Ch, Tr> Ost;
> >    ...
> >};
> >
> >template<class Ch, class Tr = char_traits<Ch> >
> >class B : public A<Ch, Tr> {
> >private:
> >    Str s;
> >    Ist ist;
> >    Ost ost;
> >    ...
> >};
>
> This is an error.  The compiler will not be able to compile the
> template code of class B.  Ideally, the compiler compiles the
> template code as much as possible and stores the result in a
> semi-compiled form as this allows for early detection of errors,
> rapid instantation of templates, and the possibily of pre-compiled
> headers.  Now what does 'Str' and 'Ist' and 'Ost' in class B
> refer to?  Perhaps to A::Str and A::Ist and A::Ost?  Unfortunately,
> one could specialize class A so that the specialized version(s) do
> not have the expected nested classes or nested typedefs.  For
> example:
>    template <> class A<int,int> { static const int Str=1; };
> In view of this, the template code in class B does not make
> sense.  One must say
>      typename A<Ch,Tr>::Str s;
>      typename A<Ch,Tr>::Ist ist;
>      typename A<Ch,Tr>::Ost ost;
> In other words, when the compiler compiles class B, it does not
> even look at class A.

Yes, but my point was that MSVC++ accepts the above code. I'm well aware
that its problematic as well as violating the standard.

>
> >template<class Ch, class Tr = char_traits<Ch> >
> >class B : public A<Ch, Tr> {
> >public:
> >    typedef A<Ch, Tr> Ts;
> >private:
> >    Ts::Str s;
> >    Ts::Ist ist;
> >    Ts::Ost ost;
> >    ...
> >};
>
> Almost right.  As one could specialize class A so that 'Str' is the
> name of a static const variable rather than a type, one must tell
> the compiler that 'Str' is the name of a type using the 'typename'
> keyword.  This is for the same three reasons as above -- early
> detection of errors, rapid instantiation of templates, possibilitis
> of pre-compiled templates.

Oops... you're right. It would help if compilers complained about this
error, though. The compilers I've tested so far, compile it without even
giving a warning.

I still think it would be nice if there were a more succinct way to safely
alias parameterised typedefs in one place.

Sam Lindley
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/05
Raw View
On 04 Aug 99 07:43:12 GMT, Sam Lindley <sam@redsnapper.net> wrote:

>I still think it would be nice if there were a more succinct way to safely
>alias parameterised typedefs in one place.

If one of the template parameters is fixed, then we could use template
typdefs, which are not supported by the standard.
   template <class Key, class Data>
   typedef std::map<Key,Data,Less<Key>,MyAllocator<...>> mymap;
This is not legal in current C++.

Would using-declarations work for your case?
   template <class T> struct A { typedef std::pair<T,T> Thing; };
   template <class T> struct B : A<T> { using A<T>::Thing; Thing t; };
I've seen this too
   template <class T>
   struct B : A<T> { typedef typename A<T>::Thing Thing; Thing t; };
What is the difference between the using-declaration method and the
typedef-typename method?

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Sam Lindley" <sam@redsnapper.net>
Date: 1999/08/06
Raw View
> >I still think it would be nice if there were a more succinct way to
safely
> >alias parameterised typedefs in one place.
>
> If one of the template parameters is fixed, then we could use template
> typdefs, which are not supported by the standard.
>    template <class Key, class Data>
>    typedef std::map<Key,Data,Less<Key>,MyAllocator<...>> mymap;
> This is not legal in current C++.

Is there any reason why not?

> Would using-declarations work for your case?
>    template <class T> struct A { typedef std::pair<T,T> Thing; };
>    template <class T> struct B : A<T> { using A<T>::Thing; Thing t; };

This is closer to what I was looking for. I forgot using-declarations could
by applied to classes just as well as namespaces!

I notice that the standard (7.3.3.5) says: "A using-declaration shall not
name a template-id". This only affects template members, so it shouldn't be
a problem.

> I've seen this too
>    template <class T>
>    struct B : A<T> { typedef typename A<T>::Thing Thing; Thing t; };

I've considered this, but its rather verbose.

> What is the difference between the using-declaration method and the
> typedef-typename method?

In this case, there appears to be no difference in behaviour. However, the
using-declaration method is obviously more concise. The single instance of
'Thing' in the using declaration, I see as a definite advantage - it more
precisely conveys the intentions of the coder. The typedef-typename method
would be useful for other sorts of aliasing - e.g. multiple versions of
Thing from different base classes. I wonder if there's some scope for
rationalisation, though...

Thanks for the tips.

Sam Lindley
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/06
Raw View
On 06 Aug 99 08:14:56 GMT, Sam Lindley <sam@redsnapper.net> wrote:

>> If one of the template parameters is fixed, then we could use template
>> typdefs, which are not supported by the standard.

>Is there any reason why not?

I have no idea why.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Sam Lindley" <sam@redsnapper.net>
Date: 1999/08/02
Raw View
When writing templatised classes, its often useful to define typedefs of
templatised types which use the enclosing templates template parameters. For
example:

template<class Ch, class Tr = char_traits<Ch> >
class A {
public:
    typedef basic_string<Ch, Tr> Str;
private:
    Str s;
    ...
};

This technique would be most useful if it were possible to centralise a
number of such typedefs in a single template, then subscribe to them
subsequently. Using MSVC++ 6 SP3, its possible to enclose templatised
typedefs in a base class and refer to them in the derived class:

template<class Ch, class Tr = char_traits<Ch> >
class A {
public:
    typedef basic_string<Ch, Tr> Str;
    typedef basic_istream<Ch, Tr> Ist;
    typedef basic_ostream<Ch, Tr> Ost;
    ...
};

template<class Ch, class Tr = char_traits<Ch> >
class B : public A<Ch, Tr> {
private:
    Str s;
    Ist ist;
    Ost ost;
    ...
};

Unfortunately, this violates section 14.6.2.3 of the standard. (This should
encourage safer code, I guess).

"
In the definition of a class template or in the definition of a member of
such a template that appears outside of the template definition, if a base
class of this depends on a template-parameter, the base class scope is not
examined during name lookup until the class template is instatiated.
[Example:

typedef double A;
template<class T> B {
    typedef int A;
};
template<class T> struct X : B<T> {
    A a;    // a has type double
};

The type name A in the definition of X<T> binds to the typedef name defined
in the global namespace scope, not to the typedef name defined in the base
class B<T>. ]
"

The other approach which I've considered, is to use a traits class (similar
to the standard char_traits class):

template<class Ch, class Tr = char_traits<Ch> >
class A {
public:
    typedef basic_string<Ch, Tr> Str;
    typedef basic_istream<Ch, Tr> Ist;
    typedef basic_ostream<Ch, Tr> Ost;
    ...
};

template<class Ch, class Tr = char_traits<Ch> >
class B : public A<Ch, Tr> {
public:
    typedef A<Ch, Tr> Ts;
private:
    Ts::Str s;
    Ts::Ist ist;
    Ts::Ost ost;
    ...
};

This isn't as elegant as the MSVC++ method, but its the best I've been able
to come up with so far.

Anyone have any better ideas...?

Sam Lindley
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1999/08/03
Raw View
On 02 Aug 99 20:55:15 GMT, Sam Lindley <sam@redsnapper.net> wrote:

>template<class Ch, class Tr = char_traits<Ch> >
>class A {
>public:
>    typedef basic_string<Ch, Tr> Str;
>    typedef basic_istream<Ch, Tr> Ist;
>    typedef basic_ostream<Ch, Tr> Ost;
>    ...
>};
>
>template<class Ch, class Tr = char_traits<Ch> >
>class B : public A<Ch, Tr> {
>private:
>    Str s;
>    Ist ist;
>    Ost ost;
>    ...
>};

This is an error.  The compiler will not be able to compile the
template code of class B.  Ideally, the compiler compiles the
template code as much as possible and stores the result in a
semi-compiled form as this allows for early detection of errors,
rapid instantation of templates, and the possibily of pre-compiled
headers.  Now what does 'Str' and 'Ist' and 'Ost' in class B
refer to?  Perhaps to A::Str and A::Ist and A::Ost?  Unfortunately,
one could specialize class A so that the specialized version(s) do
not have the expected nested classes or nested typedefs.  For
example:
   template <> class A<int,int> { static const int Str=1; };
In view of this, the template code in class B does not make
sense.  One must say
     typename A<Ch,Tr>::Str s;
     typename A<Ch,Tr>::Ist ist;
     typename A<Ch,Tr>::Ost ost;
In other words, when the compiler compiles class B, it does not
even look at class A.


>template<class Ch, class Tr = char_traits<Ch> >
>class B : public A<Ch, Tr> {
>public:
>    typedef A<Ch, Tr> Ts;
>private:
>    Ts::Str s;
>    Ts::Ist ist;
>    Ts::Ost ost;
>    ...
>};

Almost right.  As one could specialize class A so that 'Str' is the
name of a static const variable rather than a type, one must tell
the compiler that 'Str' is the name of a type using the 'typename'
keyword.  This is for the same three reasons as above -- early
detection of errors, rapid instantiation of templates, possibilitis
of pre-compiled templates.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]