Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

template for classes with specific base class

0 views
Skip to first unread message

Hao Huang

unread,
Nov 28, 1998, 3:00:00 AM11/28/98
to
Stroustrup brought this up as an questions to address in his book never
address it: If T's base class is B, instantiate; otherwise report an
error. The suggestion seems to be using specilization. But I don't have
enough knowledge to figure it out myself. Can anyone help me out?


Siemel Naran

unread,
Nov 29, 1998, 3:00:00 AM11/29/98
to
On Sat, 28 Nov 1998 19:14:17 -0500, Hao Huang

Are you saying that in a template class Class<T>, you want to impose
certain constraints on T? In this case, that T derives from a
certain base class called B. Here's an example.

Here's a typical class hierarchy:

class Fruit { public: virtual ~Fruit(); };
class Apple : public Fruit { };
class Orange : public Fruit { };

Now we want a container of fruit objects. Eg, a bag of apples or
a bag of oranges. So we make a template class FruitBag<Fruit>
where the template argument 'Fruit' is either "Apple" or "Orange"
or any class derived from "Fruit". So:

template <class Fruit>
class FruitBag { };

Here's another typical hierarchy:

class Animal { public: virtual ~Animal(); };
class Lion : public Animal { };


The problem is that the constraint that the template argument of
FruitBag be a fruit is not enforced. So this code passes compilation:

int main() { FruitBag<Lion>(); }


To enforce the constraint we can do this. We realize that if class
Derived derives from class Base, then we can static_cast a Derived*
into a Base*. So, here's an idea:

template <class TheFruit>
class FruitBag
{
static_cast<Fruit const *>(static_cast<TheFruit const *>(0));

public:
FruitBag();
};

For TheFruit==Lion, we get
static_cast<Fruit const *>(static_cast<Lion const *>(0));
which tries to cast a Lion* to a Fruit*. The cast fails and you
get a compile time error.

However, we can have an executable instruction in the class
definition. So we can try a dummy const:
static const bool dummy=
static_cast<Fruit const *>(static_cast<TheFruit const *>(0));

This is still not correct. The problem is that a static_cast between
non-integral types is not an integral constant. So instead we must
put the test inside the ctor body. That is, inside all of the ctors.
Or inside the dtor. Like this:

template <class SomeFruit>
FruitBag<SomeFruit>::FruitBag()
{
(void) static_cast<Fruit const *>(static_cast<Lion const *>(0));
}


Note that we relied on whether the conversion Derived* to Base* is
possible. In general, we can't rely on whether the conversion
Derived to Base is possible. This is because their might be a
conversion like this:

class Fruit { Fruit(const Lion&); ... };

Hence a Lion can be converted to a Fruit. This type of situation
is unlikely, of course, but it is why I used pointers.

-----

There is also the template conversion idea, which was suggested by
Andrei Alexandrescu on comp.lang.c++.moderated. It is superior
in that it gives integral constants. If the conversion is not
possible, it doesn't give a compile error, but it gives #false.
If the conversion is possible, it gives #true. One can easily
convert #false into a compile error. Eg,
const bool boolean=false;
typedef char ok[boolean];

This results in
typedef char ok[0];
But fixed size arrays of length 0 are not allowed in standard C++.
So you get a compile error.

// conversion.c
// by Siemel Naran
// idea by Andrei Alexandrescu
// Technique to see if conversions between various types are possible

namespace myspace
{

template <class From, class To>
class conversion
{
typedef char (&no )[1];
typedef char (&yes)[2];
static no check(...);
static yes check(To );
static const From& from();

public:
conversion();
static const bool exists = sizeof(check(from())) == sizeof(yes);
static const bool exists_2way = exists && conversion<To, From>::exists;
static const bool same_type = false;
};

} // namespace myspace

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

#include <typeinfo>
#include <iostream>

template <class From, class To>
void show()
{
typedef myspace::conversion<From,To> Conversion;
cout << "From==" << typeid(From).name() << '\n'
<< "To ==" << typeid(To ).name() << '\n'
<< "conversion exists? " << Conversion::exists << '\n'
<< "conversion 2way ? " << Conversion::exists_2way << '\n'
<< "\n\n";
}

struct B;
struct A { A(); };
struct B : A { B(); };

struct D;
struct C { C(); C(const D&); operator D() const; };
struct D { D(); };

int main()
{
show<A*,B*>();
show<B*,A*>();

show<C,D>();
show<D,C>();
}

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

0 new messages