Topic: Inheritance from templated base


Author: sbnaran@fermi.ceg.uiuc.edu (Siemel Naran)
Date: 1998/10/22
Raw View
On 14 Oct 98 07:07:32 GMT, AllanW@my-dejanews.com

>The code samples so far seem to have syntax errors unrelated to your
>question. But it's making it hard for me to figure out what you really
>are asking! Here, I've tried to explain some of the problems I've seen,
>and then re-cast the example code in a manner that (I hope) actually
>illustrates what you want to know.

Thank you, but that's OK.  After about one week, I've concluded that
it is a bug in Stroustrup.  There should have been a couple of
static_casts in the example.



>The question before us seems to be (correct me if I'm wrong) weather
>this is legal -- specifically, is it legal for a class to be the
>template argument for it's own base class. I suspect that the answer
>is "Yes."

The answer is indeed 'yes'.  However, the class definition of the
templated base class can't use traits of its temlated argument.


>I'm not sure if this relates to what you're asking or not, but
>the "standard" way to indicate that a base class must implement
>some function abc, is to define function abc as a pure virtual
>function:

Virtual functions are not viable in this context.  The functions
operator[] and size() are too fast.  We don't want the overhead of
a virtual func call.  We want to be able to inline them.


Just an aside, this technique is very useful for defining similar
funcs in different classes.

template <class C>
struct Oper { bool operator==(const C&) const; }

// The single arg ctor of class C should be explicit to alleviate
// the fact that Oper::operator== is a member func as opposed to a
// free func -- implicit conversion of both LHS and RHS not allowed.

class RowMatrix : public Oper<RowMatrix> { ... };
class ColMatrix : public Oper<ColMatrix> { ... };

The definition of C::operator== should be

template <class C>
bool Oper<C>::operator==(const C& that) const
{
     const C& lhs=static_cast<const C&>(*this);
     const C& rhs=that;
     if (lhs.size()!=rhs.size()) return false;
     const int N=lhs.size();
     for (int i=0; i<N; i++) if (lhs[i]!=rhs[i]) return false;
     return true;
}


The traits or interface expected from class C is this:
  it defines a function  operator[](size_type) const
  it defines a function  size() const
These traits should be documented in the class definition of Oper.

--
----------------------------------
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: sbnaran@localhost.localdomain (Siemel Naran)
Date: 1998/10/11
Raw View
On 9 Oct 1998 17:55:50 GMT, AllanW@my-dejanews.com
>In article <slrn6vereg.93u.sbnaran@localhost.localdomain>,

>> template <class C>
>> bool Oper<C>::operator==(const C& rhs) const
>> {
>>      return this->get()==rhs.get();
>> }

>You are calling member function get(), but you never defined it.
>You define this in class C, but you need a virtual declaration here.
>And the virtual declaration needs a return type, so you need that as well.

No, according to Stroustrup 13.6 this is not necessary.  He gives
an example like this,

template <class C> class Basic_Ops
{
     bool operator==(const C&) const;
     ...
};

template <class T> class Math_container : public Basic_Ops< Math_container<T> >
{
     size_t size() const;
     T& operator[](size_t); // oops: should have defined const operator[]
};

He says: "This allows the definition of the basic operations of containers
to be seperate from the definition of the containers themselves, and
defined once only".  And there's more:

template <class C>
bool Basic_Ops<C>::operator==(const C& a) const
{
     if (size()!=a.size()) return false;
     for (int i=0; i<size(); ++i)
          if ((*this)[i]!=a[i]) return false;
     return true;
}


Can someone confirm whether the above idea is really valid -- I think it
is, but neither of my compilers accepts it.


--
----------------------------------
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: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1998/10/11
Raw View
Siemel Naran <sbnaran@localhost.localdomain> writes:

> Note that when Base<Derived> is being instantiated on LINE2, class
> Derived is an incomplete type.  This means that member funcs in
> class Base that use traits in class Derived must be defined out
> of line (but they can have the 'inline' keyword).

What's the problem with defining them in-line?  They should only be
specialized on demand, anyway...

> template <class C> struct Oper
> { bool operator==(const C& rhs) const; };

> template <class C> bool Oper<C>::operator==(const C& rhs) const
> { return this->get()==rhs.get(); }

There's no method named get() in Oper<C>; perhaps that's the reason?
Perhaps you should dynamic_cast<C*>(this)...

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:oliva@gnu.org mailto:aoliva@acm.org
http://www.dcc.unicamp.br/~oliva
Universidade Estadual de Campinas, SP, Brasil
---
[ 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: "Jae Woo Kim" <kizoo@esther.donga.ac.kr>
Date: 1998/10/12
Raw View

>No, according to Stroustrup 13.6 this is not necessary.  He gives
>an example like this,
>
>template <class C> class Basic_Ops
>{
>     bool operator==(const C&) const;
>     ...
>};


I think operator == should be virtual

>template <class T> class Math_container : public Basic_Ops<
Math_container<T> >
>{
>     size_t size() const;
>     T& operator[](size_t); // oops: should have defined const operator[]
>};
>
>He says: "This allows the definition of the basic operations of containers
>to be seperate from the definition of the containers themselves, and
>defined once only".  And there's more:


>template <class C>
>bool Basic_Ops<C>::operator==(const C& a) const
>{
    // Work Just for random-accessible containers ... BUT type parameter
<class C>  is too general
    // for the purpose. Can we specialize the defintion operator== on
Math_container<T> ?
>}


>Can someone confirm whether the above idea is really valid -- I think it
>is, but neither of my compilers accepts it.


I'm not sure the idea really helps to reuse the shared operation. IMHO, the
above approach is much like the F-bounded parameterization, which removes
inherent type unsafety of subtyping relation in contravariant situation of
binary methods. Consequently I don't think it is not a recommended practice
that F-bounded parameterization is used for code reuse. See the following
example.

template<typename T>
struct Eq {
 virtual bool operator==(const T& rhs) const = 0;
    // I think T should be Eq<T>, but off-topic for this thread.
};

template<typename T>
struct Ord : Eq<T> {
 virtual bool operator<(const T& rhs) const = 0;
    // likewise Ord<T>
};

struct Thing : Ord<Thing>
{
 bool operator==(const Thing& rhs) const { ... } // Eq<Thing>
 bool operator<(const Thing& rhs) const { ... } // Ord<Thing>
};

The cited practice is a useful pattern to ensure the type safety of binary
methods, but I didn't see any useful application for code reuse yet. Am I
misleading ?

KiZOo


[ 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.COM (Siemel Naran)
Date: 1998/10/12
Raw View
On 11 Oct 98 11:01:04 GMT, Alexandre Oliva <oliva@dcc.unicamp.br> wrote:
>Siemel Naran <sbnaran@localhost.localdomain> writes:

>> Note that when Base<Derived> is being instantiated on LINE2, class
>> Derived is an incomplete type.  This means that member funcs in
>> class Base that use traits in class Derived must be defined out
>> of line (but they can have the 'inline' keyword).

>What's the problem with defining them in-line?  They should only be
>specialized on demand, anyway...

Yes, I think you're right here.  But you can't use traits of the
Derived class in the Base class itself.  IOW, the definition of
class Oper can't use traits of class C.  Eg,

template <class C>
struct Oper { typedef typename C::value_type value_type; };

class Thing : public < Oper<Thing> > { typedef int value_type; };
// error: instantiation of class definition Oper<Thing> tries to
// use Thing::value_type, but Thing is an incomplete type



>> template <class C> bool Oper<C>::operator==(const C& rhs) const
>> { return this->get()==rhs.get(); }

>There's no method named get() in Oper<C>; perhaps that's the reason?
>Perhaps you should dynamic_cast<C*>(this)...

Nope.  The class can't even be assumed to have a vptr.  Look at the
example in Stroustrup 13.6.  That's precisely where I got my example
from.  If the example is valid (and I strongly doubt Stroustrup has
made a mistake here as this is a big conceptual point), then this
technique could be very powerful as it saves us from having to write
a lot of code.


--
----------------------------------
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: blargg@flash.net (Gargantua Blargg)
Date: 1998/10/12
Raw View
In article <or67dtegbl.fsf@araguaia.dcc.unicamp.br>, Alexandre Oliva
<oliva@dcc.unicamp.br> wrote:

> Siemel Naran <sbnaran@localhost.localdomain> writes:
>
> > Note that when Base<Derived> is being instantiated on LINE2, class
> > Derived is an incomplete type.  This means that member funcs in
> > class Base that use traits in class Derived must be defined out
> > of line (but they can have the 'inline' keyword).
>
> What's the problem with defining them in-line?  They should only be
> specialized on demand, anyway...

Argh. Siemel wants to inject member functions from a template base class
into a derived class, yet allow the functions in the base class to call
derived-class member functions.

    template<class Derived>
    struct inject_operator {
        bool same_as_this_one( Derived const& d )
        {
            return static_cast<Derived&> (*this).get() == d.get()
        }
    };

    struct Derived : inject_operator<Derived> {
        int* get() const;
    };

    void f( Derived const& d1, Derived const& d2 )
    {
        if ( d1.same_as_this_one( d2 ) )
            // ...
    }

This allows an inline template implementation of the common code without
any virtual functions.

Note the static_cast<> (to the derived class... duh! that's what
static_cast<> is for). This was missing from Siemel's code before.

> > template <class C> struct Oper
> > { bool operator==(const C& rhs) const; };
>
> > template <class C> bool Oper<C>::operator==(const C& rhs) const
> > { return this->get()==rhs.get(); }
>
> There's no method named get() in Oper<C>; perhaps that's the reason?
> Perhaps you should dynamic_cast<C*>(this)...

Close. A better response than "add a virtual get() to the base class". But
no, we don't want the overhead of a dynamic_cast<>. Heck, it wouldn't work
anyway, because there are no virtual functions in Oper<C>.

--
blarggflash.net | Gargantua Blargg | http://www.flash.net/~blargg/
---
[ 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: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1998/10/12
Raw View
Siemel Naran <sbnaran@localhost.localdomain> writes:

> On 9 Oct 1998 17:55:50 GMT, AllanW@my-dejanews.com

>> You are calling member function get(), but you never defined it.
>> You define this in class C, but you need a virtual declaration
>> here.  And the virtual declaration needs a return type, so you need
>> that as well.

> No, according to Stroustrup 13.6 this is not necessary.

Unless you're just misreading that part, it is wrong.

> He gives an example like this,

> template <class C> class Basic_Ops
> {
>      bool operator==(const C&) const;
>      ...
> };

Isn't there something like `virtual typename C::value_type
operator[](size_t) const = 0;' in Basic_Ops?

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:oliva@gnu.org mailto:aoliva@acm.org
http://www.dcc.unicamp.br/~oliva
Universidade Estadual de Campinas, SP, Brasil
---
[ 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: "Jae Woo Kim" <kizoo@esther.donga.ac.kr>
Date: 1998/10/12
Raw View

Gargantua Blargg =C0=CC(=B0=A1) <199810111505.KAA08039@centurion.flash.ne=
t>
=B8=DE=BD=C3=C1=F6=BF=A1=BC=AD =C0=DB=BC=BA=C7=CF=BF=B4=BD=C0=B4=CF=B4=D9=
...

>Argh. Siemel wants to inject member functions from a template base class
>into a derived class, yet allow the functions in the base class to call
>derived-class member functions.

I cannot think subtyping for code injection would result in good pratice.
>
>    template<class Derived>
>    struct inject_operator {
>        bool same_as_this_one( Derived const& d )
>        {
>            return static_cast<Derived&> (*this).get() =3D=3D d.get()
    // Is it reasonable to use 'static_cast'ing here? Somewhat strange to
me.
>        }
>    };

If you don't need dynamic binding, why not just try to define a generic O=
p?

namespace shared_op {

template<class T>
bool same_as_this_one(const T& lhs, const T& rhs)
{
    return lhs.get() =3D=3D rhs.get(); // no casting
}

}

In what situation your approach is preferred?

>
[snip]
>
>
>This allows an inline template implementation of the common code without
>any virtual functions.


>Note the static_cast<> (to the derived class... duh! that's what
>static_cast<> is for). This was missing from Siemel's code before.


Still strange to me.

>> > template <class C> struct Oper
>> > { bool operator=3D=3D(const C& rhs) const; };
>>
>> > template <class C> bool Oper<C>::operator=3D=3D(const C& rhs) const
>> > { return this->get()=3D=3Drhs.get(); }


What does Oper class (type) means? Shared code block?


>Close. A better response than "add a virtual get() to the base class". B=
ut
>no, we don't want the overhead of a dynamic_cast<>. Heck, it wouldn't wo=
rk
>anyway, because there are no virtual functions in Oper<C>.


As you've pointed out, the cited practice is not much advantageous, I thi=
nk.

Jaewoo Kim, Dong-A Univ. Pusan Korea.



[ 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: 1998/10/12
Raw View
On 12 Oct 98 04:19:23 GMT, Alexandre Oliva <oliva@dcc.unicamp.br> wrote:
>Siemel Naran <sbnaran@localhost.localdomain> writes:

>Isn't there something like `virtual typename C::value_type
>operator[](size_t) const = 0;' in Basic_Ops?

There is no such func in Stroustrup's example.  He does use "..." in
the class definition, so we don't know what's missing.  But I don't
think he would have left out the definition of operator[], as it is
important for the example.  There are no virtual funcs either.  And
to address Gargantua's point, Stroustrup does not use static_cast
either, although the static_cast does make sense to me.

Finally, note that from our previous exchange, we can't use typedefs
from the templated class in the class definition of the operation
class -- ie, "typename C::value_type Oper<C>::operator[] const" is a
semantic error as C is an incomplete type.  So we couldn't have such
a function anyway.

-----

Here's the example again,

template <class C>
struct Basic_ops
{
     bool operator==(const C&) const; // *this has type  const Basic_ops&
};

template <class C>
bool Basic_ops<C>::operator==(const C& that) const
{
     if (this->size()!=that.size()) return false;
     for (int i=0; i<size(); i++) if ((*this)[i]!=that[i]) return false;
     return true;
}

// notice the traits or expected interface!
// C is expected to have a size() member function, as well as a const op[]


template <class T>
class Math_container : public Basic_ops< Math_container<T> >
{
     public:
          size_t size;
          T operator[](size_t) const;
};


-----

How does the compiler parse this?

It reads "template<class T> class Math_container".  So it knows that
a thing Math_container<T> exists and that it is a class.  IOW, we
have here a forward declaration of the class Math_container<T>.  This
class is not defined though; it is an incomplete type.

The compiler reads " : public Basic_ops< Math_container<T> >".  So
now it tries to build the template.  Recall that Math_container<T> is
an incomplete type.  The building of Basic_ops<Math_container<T>> works
because the definition of class Basic_ops<C> does not use traits
defined in class C.  Ie, class C is only used by reference or pointer.
It is not used by value.  We also don't use typedefs defined in class
C.

So then, we have this:

template <class T>
class Math_container
{
     public:
     bool operator==(const Math_container<T>&) const;
        // *this has type  const Basic_ops<Math_container<T>>&
        // for some reason, *this is also  const Math_container<T>&
        // this doesn't make sense to me!
     ...
};

If someone can explain the comments above, then all is fine.


Now, the compiler parses the rest of the class and we get:

template <class T>
class Math_container
{
     public:
          bool operator==(const Math_container<T>&) const;
          size_t size;
          T operator[](size_t) const;
};


When it instantiates the op== function for T==double, C=Math_container<T>,
we get this:

bool
Basic_ops<Math_container<double>>::operator==
          (const Math_container<double>& that)
     const
{
     if (this->size()!=that.size()) return false;
     for (int i=0; i<size(); i++) if ((*this)[i]!=that[i]) return false;
     return true;
}


I don't think this should work as is.  What we really need is this here:

bool
Math_container<double>::operator==
          (const Math_container<double>& that)
     const
{
     ...
}


So what works?  Is Stroustrup's example correct?


--
----------------------------------
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: Gabriel Dos Reis <dosreis@DPTMaths.ens-cachan.fr>
Date: 1998/10/12
Raw View
>>>>> Jae Woo Kim <kizoo@esther.donga.ac.kr> wrote:

[...]

>> Argh. Siemel wants to inject member functions from a template base class
>> into a derived class, yet allow the functions in the base class to call
>> derived-class member functions.

> I cannot think subtyping for code injection would result in good pratice.


Why do you think so? Could you elaborate please ?


>>
>> template<class Derived>
>> struct inject_operator {
>> bool same_as_this_one( Derived const& d )
>> {
>> return static_cast<Derived&> (*this).get() =3D3D=3D3D d.get()
>     // Is it reasonable to use 'static_cast'ing here? Somewhat strange to
> me.


The issue whether it is reasonnable or not *depends* a lot on the
programmer's goals and uses. Is it reasonable to use 'goto's in programs?


>> }
>> };

> If you don't need dynamic binding, why not just try to define a generic O=3D
> p?


Maybe does he want to define a set of interface similar to abstract
classes without dynamic binding because the binding aren't truly
dynamic: it just unknown at the base class definition point.


> namespace shared_op {

> template<class T>
> bool same_as_this_one(const T& lhs, const T& rhs)
> {
>     return lhs.get() =3D3D=3D3D rhs.get(); // no casting
> }

> }

> In what situation your approach is preferred?


I have been using and promoting similar idiom for nearly a year in
situations where I need to define interfaces (you know those things
which best approximate _types_, not classes) without dynamic binding
(because my compilers aren't smart enough to optimise away virtual
calls, or just because virtuals aren't relevant). It has proven to be
useful and powerful. I've written a paper (I were lazy to polish)
demonstrating how this idiom can effectively  be used to emulate in
some sense the signature extension found in the GNU compiler.

>>
> [snip]
>>
>>
>> This allows an inline template implementation of the common code without
>> any virtual functions.


>> Note the static_cast<> (to the derived class... duh! that's what
>> static_cast<> is for). This was missing from Siemel's code before.


> Still strange to me.


Not so to me.

--
Gabriel Dos Reis                   |  Centre de Math=E9matiques et de
dosreis@cmla.ens-cachan.fr         |         Leurs Applications
Fax : (33) 01 47 40 21 69          |   ENS de Cachan -- CNRS (URA 1611)
               61, Avenue du Pdt Wilson, 94235 Cachan - Cedex


[ 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: "Jae Woo Kim" <kizoo@esther.donga.ac.kr>
Date: 1998/10/13
Raw View

Gabriel Dos Reis =C0=CC(=B0=A1) =B8=DE=BD=C3=C1=F6=BF=A1=BC=AD =C0=DB=BC=BA=
=C7=CF=BF=B4=BD=C0=B4=CF=B4=D9...

> > I cannot think subtyping for code injection would result in good pratice.
> Why do you think so? Could you elaborate please ?

Code injection is nothing more than code reuse, say, subclassing in this case.
In other words, subtyping for code injection is nearly the same as subtyping
for subclassing,
where code reuse by subclassing is a side-effect of subtyping inheritance,
public, in C++.


>> template<class Derived>
>> struct inject_operator {
>> bool same_as_this_one( Derived const& d )
>> {
>> return static_cast<Derived&> (*this).get() =3D3D=3D3D d.get()
>     // Is it reasonable to use 'static_cast'ing here? Somewhat strange to
> me.
> The issue whether it is reasonnable or not *depends* a lot on the
> programmer's goals and uses. Is it reasonable to use 'goto's in programs?

Yes, sometimes we need goto, casting for handling complex problems in real
world.
Just I think this is not the case. The point that we should care about
public derivation in C++,
bring 2 effects, that should be separated, into the language environment;
subclassing and subtyping.

> Maybe does he want to define a set of interface similar to abstract
> classes without dynamic binding because the binding aren't truly
> dynamic: it just unknown at the base class definition point.


What do we call a set of interface? How to express in C++?
AFAIK, 2 methods are possible in C++; explicit and implicit.

(1) Explicit grouping of interface is the defintion of a "class".
(2) Implicit grouping is a type parameterization, T, whose syntax checking
is deferred to its instantiation time.


Again If we want to avoid dynamic binding, I would prefer (2). In C++, a
flaw of (2) is we cannot impose proper type constraints on the defintion of
generic op. except ugly syntax err msg.
C++ doesn't provide the explicit Bounded parameterization, but ironically no
explicit type constraint mechanism gives C++  more expressiveness and
effieciency for generic polymorphism.

> In what situation your approach is preferred?

> I have been using and promoting similar idiom for nearly a year in
> situations where I need to define interfaces (you know those things
> which best approximate _types_, not classes) without dynamic binding
> (because my compilers aren't smart enough to optimise away virtual
> calls, or just because virtuals aren't relevant). It has proven to be
> useful and powerful.

We call such practice generic programming, doesn't we? In C++, interface is
implicit.
Yes, I'd like to have a explicit interface properly encapsulated but that's
the way C++ goes;
efficiency and flexibility.

> I've written a paper (I were lazy to polish)
> demonstrating how this idiom can effectively  be used to emulate in
> some sense the signature extension found in the GNU compiler.


Can you explain the idiom in briefly? I'd really like to see it, cause I
haven't yet seen any idiom that could replace signature facility
satisfactory.

>> This allows an inline template implementation of the common code without
>> any virtual functions.


> Still strange to me.
> Not so to me.

Do we really need static_cast<T>(e)? Again, how much more advantageous than
simple generic ops?

Jaewoo Kim, Dong-A Univ. Pusan Korea.



[ 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: AllanW@my-dejanews.com
Date: 1998/10/14
Raw View
The code samples so far seem to have syntax errors unrelated to your
question. But it's making it hard for me to figure out what you really
are asking! Here, I've tried to explain some of the problems I've seen,
and then re-cast the example code in a manner that (I hope) actually
illustrates what you want to know.

> On 12 Oct 98 04:19:23 GMT, Alexandre Oliva <oliva@dcc.unicamp.br> wrote:
> >Isn't there something like `virtual typename C::value_type
> >operator[](size_t) const = 0;' in Basic_Ops?

In article <slrn6vpc0a.jer.sbnaran@localhost.localdomain>,
  sbnaran@KILL.uiuc.edu wrote:
> There is no such func in Stroustrup's example.
[snip]
> Here's the example again,
[Some code has been reformatted]
>
> template <class C>
> struct Basic_ops

  // Note that Basic_ops<C> is not derived from anything

> {
>      bool operator==(const C&) const;
>      // In operator==, *this has type const Basic_ops&
> };
>
> template <class C>
> bool Basic_ops<C>::operator==(const C& that) const
> {
>      if (this->size()         != that.size())
       //  ^^^^^^^^^^^^            ^^^^^^^^^
       //  Basic_ops<C>::size      C::size must be defined
       //  is not defined!         as noted below

>          return false;
>      for (int i=0; i<size(); i++)
           //          ^^^^^^ Calls Basic_ops<C>::size if defined,
           //                 otherwise uses size() in some other
           //                 current scope (if any) or syntax error
>          if ((*this)[i]                != that[i])
           //  ^^^^^^^^^^                   ^^^^^^^
           //  Basic_ops<C>::operator[]     C::operator[] must be
           //  is not defined!              defined as noted below

>              return false;
>      return true;
> }
>
> // notice the traits or expected interface!
> // C is expected to have a size() member function,
> // as well as a const op[]

  // Yes, but what about Basic_ops<C>::size and
  // Basic_ops<C>::operator[] ?
  // They are also being called, but do not exist!

> template <class T>
> class Math_container
>     : public Basic_ops< Math_container<T> >
      //       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Deriving Math_container<T>
      //       from Basic_ops<Math_container<T> > is apparently what
      //       you're trying to ask about. But since Basic_ops<C> is
      //       badly defined, it clouds the issue.
> {
>      public:
>           size_t size;
            //     ^^^^ Member variable named size, further confusing
            //          the issue. size() will be a syntax error!

>           T operator[](size_t) const;
> };
>
[snip]
>
> template <class T>
> class Math_container
> {
       // What's happened here? You ended the definition of
       // Math_container<T>, then started another conflicting
       // definition? This version isn't derived from anything?

>      public:
>      bool operator==(const Math_container<T>&) const;
>         // *this has type  const Basic_ops<Math_container<T>>&
>         // for some reason, *this is also  const Math_container<T>&
>         // this doesn't make sense to me!

          // Actually, *this has type const Math_container<T>, which
          // is *DERIVED* from Basic_ops<Math_container<T> >, thus
          // inheriting any base member functions. Which would have
          // included Basic_ops<C>, except that
          //  (1) This function would override it, and
          //  (2) Basic_ops<C> was ill-formed in the first place
>      ...
> };
>
[snip]
>
> template <class T>
> class Math_container
> {
       // Note: third conflicting definition

>      public:
>           bool operator==(const Math_container<T>&) const;
>           size_t size;
>           T operator[](size_t) const;
> };
>
[snip]
> bool
> Basic_ops<Math_container<double>>::operator==
>           (const Math_container<double>& that)
>      const
> {
>      if (this->size()!=that.size()) return false;
>      for (int i=0; i<size(); i++) if ((*this)[i]!=that[i]) return false;
>      return true;
> }

Let's try to reorganize it all.

    template <class C> struct Basic_ops {
        bool operator==(const C&) const;
    };

    template <class C>
    bool Basic_ops<C>::operator==(const C& that) const {
        // I'm not really sure what we're trying to accomplish here.
        // How can the container C be equivalent to the basic
        // operation of operator==? Stub this out until I understand
        // it better -- this will allow us to focus on the other
        // questions you were asking.
        return false;
    }

    template <class T> class Math_container
        : public Basic_ops<Math_container<T> > {
        size_t m_size;
        T *m_data;
    public:
        Math_container(size_t size)
            : m_size(size), m_data(new T[size]) {}
        ~Math_container() { delete m_data; }
        size_t size() { return m_size; }
        T operator[](size_t i) const { return m_data[i]; }
        bool operator==(const Math_container<T>&) const;
    };

    template <class T> bool
    Math_container<T>::operator==(const Math_container<T>&that) const {
        // Apparently trying to see if two containers
        // hold the same data, in the same order
        if (size() != that.size())
            return false;
        for (size_t i=0; i<size(); ++i)
            if ((*this)[i] != that[i])
                return false;
        return true;
    }

    Math_container<int> int_container;


The question before us seems to be (correct me if I'm wrong) weather
this is legal -- specifically, is it legal for a class to be the
template argument for it's own base class. I suspect that the answer
is "Yes."

I'm sure that there's at least one thing about what I just wrote,
and perhaps several things, that are different than what you want
to discuss. Please use this as a starting place; modify it in
whatever way illustrates your point.

I'm not sure if this relates to what you're asking or not, but
the "standard" way to indicate that a base class must implement
some function abc, is to define function abc as a pure virtual
function:
    virtual bool abc() const = 0;
This might apply to Basic_op::operator== or to some function(s)
called from Basic_op::operator==, depending on what you really
want that function to do and how you want it to do it.

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ 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: AllanW@my-dejanews.com
Date: 1998/10/14
Raw View
In article <199810111505.KAA08039@centurion.flash.net>,
  blargg@flash.net (Gargantua Blargg) wrote:
> In article <or67dtegbl.fsf@araguaia.dcc.unicamp.br>, Alexandre Oliva
> <oliva@dcc.unicamp.br> wrote:
> > There's no method named get() in Oper<C>; perhaps that's the reason?
> > Perhaps you should dynamic_cast<C*>(this)...
>
> Close. A better response than "add a virtual get() to the base class". But
> no, we don't want the overhead of a dynamic_cast<>. Heck, it wouldn't work
> anyway, because there are no virtual functions in Oper<C>.

We have a class, call it ABC. Class ABC shouldn't ever be instanciated
directly; instead, it will be used as a base class for other, more
concrete classes. Class ABC does have member functions, though, which
will be inherited by the concrete classes.

One of ABC's classes needs to call member function get() in the
derived classes. There's one catch: function get() needs a different
definition in each of the base classes. But we're going to insist that
each of the base classes must define this function.

Perhaps We could do this with complicated template semantics, plus
lots of documentation, and get it to work right. But isn't this a
textbook example of *EXACTLY* what pure virtual functions were designed
for? Why is this a poor response? Why go the complicated route when the
simple route is just as fast, much easier, and well documented already?
If you wouldn't use pure virtual functions in this case, when WOULD you
use them?

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ 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: "Jae Woo Kim" <kizoo@esther.donga.ac.kr>
Date: 1998/10/15
Raw View

Siemel Naran =C0=CC(=B0=A1) =B8=DE=BD=C3=C1=F6=BF=A1=BC=AD =C0=DB=BC=BA=C7=
=CF=BF=B4=BD=C0=B4=CF=B4=D9...

[snip]

>bool
>Basic_ops<Math_container<double>>::operator==
>          (const Math_container<double>& that)
>     const
>{
>     if (this->size()!=that.size()) return false;
>     for (int i=0; i<size(); i++) if ((*this)[i]!=that[i]) return false;
>     return true;
>}

In Stroustrup's 3rd Ed., I've seen 2 similar examples in terms of template
class usage. IMO, one is a slightly deviated C++ coding of F-bounded
parameterization if I correctly remember.

template<class T> struct Eq {
friend operator==(const T& lhs, const T& rhs);
};

template<class T> struct Ord : Eq<T> {
friend operator<(const T& lhs, const T& rhs);
};

where any template class, like Eq, Ord is treated a type function, F.

>I don't think this should work as is.  What we really need is this here:
>
>bool
>Math_container<double>::operator==
>          (const Math_container<double>& that)
>     const
>{
>     ...
>}
>
>
>So what works?  Is Stroustrup's example correct?

In the above example, Eq<T>, Ord<T> represent templatized Interface. First
examine the following case.

template<class EqComp>
bool equals(const EqComp& lhs, const EqComp& rhs)
{
    return lhs == rhs;
}

class Thing : public Eq<Thing> {
    ...
};

bool operator==(const Thing& lhs, const Thing& rhs) { ... }

All Thing instances could be applied to _equals_. No problems because
operator== is friend and then not a binary method in this case. BUT the
following alternative would be possible. Binary method problem is introduced
and that's make it difficult that we should declare the type of _rhs_ param.

template<class T>
struct Eq {
    virtual bool operator==(const T& rhs) =0;
};

class Thing : Eq<Thing> {
    ...
    bool operator==(const Thing& rhs) { ... } // overrides
    ...
};

The power of F-bounded param needs no RTTI satisfying subtype relation. The
method operator== requires that both RHS and LHS of method must be the same
type, but subtype relation that RHS must be contravariant, in this case, Eq.
We cannot avoid RTTI.

I found the above approach is very satisfactory , but have a serious flaw.

template<class EqComp>
bool equals(const Eq<EqComp>& lhs, const Eq<EqComp>& rhs)
{
    return lhs == rhs;
}

equals(Thing(...), Thing(...)); // Oops, Fail to find type-matching method
!!

Consequently, I think the RHS should be Eq<T> either.

    Thing x(..);
    x == Thing(...); // No problem because Thing is a subtype of Eq<T>

    equals(Thing(...), Thing(...)); // No problem because ==  now requires
Eq<Thing>, no Thing.

Jae Woo Kim
Dong-A Univ., Pusan, Korea.



[ 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: efrank@upenn5.hep.upenn.edu (Ed Frank)
Date: 1998/10/08
Raw View
Put simply, in what circumstances does the standard allow the following
pattern:
 template <class T>
 class Base {
    // stuff
 };

 class Derived : public Base<Derived> {
 }

The issue is a class, Derived, deriving from a class template
instantiated on itself.

I have played with this on several compilers and get it to compile
when //stuff only needs a T* or T&, but it fails when you
actually have a T in Base<T>, i.e., need to know sizeof(T).  At
least, that is what it looks like.

So, all guessing aside, what exactly is allowed?  I've tried to
look at the standard, but I guess I'm not smart enough to read
that thing and be convinced I've understood what I've read.

 -Ed
---
[ 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: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1998/10/08
Raw View
Ed Frank <efrank@upenn5.hep.upenn.edu> writes:

> Put simply, in what circumstances does the standard allow the following
> pattern:
>  template <class T>
>  class Base {
>     // stuff
>  };

>  class Derived : public Base<Derived> {
>  }

This is allowed wherever:

class Derived;
class Base {
  typedef Derived T;
  // stuff
};
class Derived : public Base {
};

is acceptable.

> I have played with this on several compilers and get it to compile
> when //stuff only needs a T* or T&, but it fails when you
> actually have a T in Base<T>, i.e., need to know sizeof(T).

T* and T& do not require T to be a complete type, but sizeof does.
Since the point of instantiation of Base<Derived> is before Derived
becomes a complete type, you can only depend on T being a complete
type within Base's member functions and initializers of static data
members.

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:oliva@gnu.org mailto:aoliva@acm.org
http://www.dcc.unicamp.br/~oliva
Universidade Estadual de Campinas, SP, Brasil


[ 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: 1998/10/09
Raw View
On 08 Oct 98 02:33:01 GMT, Ed Frank <efrank@upenn5.hep.upenn.edu> wrote:

>Put simply, in what circumstances does the standard allow the following
>pattern:
> template <class T> // LINE1
> class Base {
>    // stuff
> };
>
> class Derived : public Base<Derived> { // LINE2
> }

This should work.  There is an example of this in Stroustrup III.
Note that when Base<Derived> is being instantiated on LINE2, class
Derived is an incomplete type.  This means that member funcs in
class Base that use traits in class Derived must be defined out
of line (but they can have the 'inline' keyword).  Also, class Base
can't use typedefs defined in class T.

However, I can't get this to compile:

-----

#include <iostream>

template <class C>
struct Oper
{
     bool operator==(const C& rhs) const;
};

template <class C>
bool Oper<C>::operator==(const C& rhs) const
{
     return this->get()==rhs.get();
}

/////
/////
/////

template <class T>
class MyClass : public Oper< MyClass<T> >
{
     private:
          typedef T value_type;
          value_type value;
     public:
          explicit MyClass (value_type value_) : value(value_) { }
          value_type get() const { return value; }
};

int main()
{
     MyClass<int> c(3),d(4);
     std::cout << std::boolalpha;
     std::cout << (c==c); // should print "true"
     std::cout << (c==d); // should print "false"
}


--
----------------------------------
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: AllanW@my-dejanews.com
Date: 1998/10/09
Raw View
In article <slrn6vereg.93u.sbnaran@localhost.localdomain>,
  sbnaran@KILL.uiuc.edu wrote:
> However, I can't get this to compile:
>
> #include <iostream>
>
> template <class C>
> struct Oper
> {
>      bool operator==(const C& rhs) const;
> };
>
> template <class C>
> bool Oper<C>::operator==(const C& rhs) const
> {
>      return this->get()==rhs.get();
> }
You are calling member function get(), but you never defined it.
You define this in class C, but you need a virtual declaration here.
And the virtual declaration needs a return type, so you need that as well.
Try replacing Oper with this:
    template <class C, class V> // Added class V
    struct Oper
    {
         bool operator==(const C& rhs) const;
         virtual V get() const=0; // Added this line
    };

    template <class C, class V> // Added class V
    bool Oper<C,V>::operator==(const C& rhs) const // Added V
    {
         return this->get()==rhs.get();
    }




> template <class T>
> class MyClass : public Oper< MyClass<T> >
Change to match above definition:
  class MyClass : public Oper< MyClass<T>,T >
> {
>      private:
>           typedef T value_type;
>           value_type value;
>      public:
>           explicit MyClass (value_type value_) : value(value_) { }
>           value_type get() const { return value; }
> };
>
> int main()
> {
>      MyClass<int> c(3),d(4);
>      std::cout << std::boolalpha;
>      std::cout << (c==c); // should print "true"
>      std::cout << (c==d); // should print "false"
> }

This version works on VC5.0

--
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ 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              ]