Max Well
Max Well

Reputation: 41

Array of template objects

I don't know how to resolve a problem with templates and inheritance.

In my code there is a templated class which looks more or less like:

template<typename T>
class Derived : public Base{
     T value;
public:
     Derived(T arg) { value=arg; };
     T getValue() { return value;};
};

class Base{
};

The only purpose of my Base class is to group an array of objects of the Derived class. The parameter T is generally double, float or complex, although int and structs might also become useful. (There should be several more similar derived classes with a few additional functions, later.)

I can create such a group

Base** M = new Base*[numElements];

and assign elements of the derived class to them, e.g.:

M[54] = new Derived<double>(100.);

But how can I find out that the 55th element has value 100 later? I would need something like

virtual T getValue() = 0;

but T is a typename of the derived class and may be different for any two elements of this array.

Upvotes: 4

Views: 7149

Answers (6)

jon hanson
jon hanson

Reputation: 9408

You could add overloaded has_value() methods to the Base class:

class Base
{
public:
    virtual ~Base () {}
    virtual bool has_value (int i) {return false;}
    virtual bool has_value (double d) {return false;}
    virtual bool has_value (const std::string& s) {return false;}
    // etc.
};

one of which you which you override in the Derived class:

template<typename T>
class Derived : public Base
{
     T value;
public:
     Derived(T arg) {value=arg;}
     T getValue() { return value;}

     virtual bool has_value (T t)
     {
         return t == value;
     }
};

E.g.:

bool test ()
{
    std::vector<Base*> bases;
    bases.push_back (new Derived<double> (1.234));
    bases.push_back (new Derived<int> (100));
    bases.push_back (new Derived<std::string> ("zap"));

    for(std::vector<Base*>::const_iterator iter = bases.begin (); iter != bases.end (); ++iter)
        if ((*iter)->has_value (100))
            return true;
    return false;
}

Note, you can't replace the has_value methods in the Base class with a single templated method as virtual methods can't be templated.

Upvotes: 2

navigator
navigator

Reputation: 1596

use boost::any to store the objects in the array. Then when you want to operate on it, you can use boost::any_cast to the possible types you have.

Upvotes: 0

vines
vines

Reputation: 5225

You can use dynamic_cast to know what a type is it (in addition to what @StackedCrooked says). It would require some virtual functions defined in the base class, but you already need a virtual destructor there (to have the ability to delete values through base class pointers).

As an alternative, you might try boost::variant or boost::any :)

Upvotes: 0

Ben Voigt
Ben Voigt

Reputation: 283793

The Visitor pattern is probably your best bet here. The code iterating the array has to provide a "Visitor" object that knows how to handle each of the various types.

struct NumericVisitor
{
    virtual void visit(double) = 0;
    virtual void visit(int) = 0;
    virtual void visit(unsigned char) = 0;
};

struct Visitable
{
    virtual void visitValue(NumericVisitor& visitor) = 0;
};

template<typename T>
class Derived : public Visitable
{
     T value;
public:
     Derived(const T& arg) value(arg) {}
     void visitValue(NumericVisitor& visitor) { visitor.visit(value); }
};

Now you can define visitors for each operation you want to do on the collection, e.g. convert each element to a string, with a different format for each type, or store each element into a file, where each type can take up a different amount of space.

Upvotes: 4

iammilind
iammilind

Reputation: 70078

NO. It's not possible practically to have such function, because of 2 reasons:

  1. Base can't be template, as you want a generic handle to store an array which can contain any type of Derived like <int>, <double>, <float>, any struct <abc>.
  2. You can not have a template virtual method inside Base, because language doesn't allow it

Plain simple way to solve this problem is to have "getter" method for every type like get_double(), get_int(), get_float(), get_abc() and so on. However, your Base will be cluttered with such methods.

Upvotes: 0

StackedCrooked
StackedCrooked

Reputation: 35515

Add a method "getDouble" to your base class. The derived classes must then implement this method and cast their own type to double if required.

Upvotes: 0

Related Questions