4

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.

Max Well
  • 41
  • 1
  • 2
  • 1
    This seems very similar to [this question](http://stackoverflow.com/questions/5785586/storing-vector-of-stdshared-ptrfoo-where-foo-is-a-templated-class). – hifier May 01 '11 at 02:13

6 Answers6

4

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.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Hello Ben, thank you for your reply to my first question. I'm sure you are one of the greater experts on this forum and using a Visitor pattern looks more promising than adding getter functions for all types without knowing which one to use.Though, I must admit that I don't understand it completely, yet. Does it do what I want? Converting elements to strings or storing elements into a file seems to be already possible with my original approach, but do I also overcome the remaining difficulties? – Max Well Apr 30 '11 at 19:55
  • @Max: Of course it's possible to add any operation to the Base class as a virtual function. What the Visitor pattern enables is (1) adding new operations without cluttering `class Base` and (2) overcome the restriction on template specialization of member functions (you can't specialize members of a template class, you have to specialize the whole class). I think `Visitor` will meet your needs, but your example usage "find out that the element has value 100" isn't clear enough for me to write you a good sample. – Ben Voigt Apr 30 '11 at 20:28
  • @Ben: The example usage is quite simple: the base-class pointer is known and I want to retrieve the value of a member variable of a derived class without knowing its type. I guess that there is no easy way around. (Maybe, in some years one may be allowed to write `code` virtual auto getValue()` in the base class?) – Max Well May 01 '11 at 06:35
  • @Max: That's not usage. Retrieving the value is not useful, unless you do something with the value. Otherwise I could give you the value as a `void*`. Give an example of something you want to do with the value, and then I can give better sample code. – Ben Voigt May 01 '11 at 13:13
  • @Max: The purpose of the Derived class is to contain boundary conditions of a program which needs to solve a partial differential equation. I think that I will add a number of get() functions for different types and one which requests the type. Alternatively, I could possibly write `void doSomething(Matrix &LHS, Vector& rhs)`. – Max Well May 02 '11 at 05:34
  • @Max: What types can your solver handle besides `std::complex`? If you need individual elements to represent vectors and matrices, can you make everything a mxn matrix (where a vector is 1xn and a scalar is 1x1? That works very well for e.g. MatLab. – Ben Voigt May 02 '11 at 13:07
  • @Ben: None yet. I had written a code which solves problems in `double` precision, but I want a more flexible program now. There is no need for MatLab, although its solvers remain much superior to my naive Numerical Recipes & Co. solvers. (It's just an educational hobby project.) – Max Well May 02 '11 at 18:02
  • @Max: I wasn't suggesting to use MatLab instead of your solver, I was suggesting to observe its design. A system that is powerful enough for MatLab is probably a good starting point for a different-but-related custom project. – Ben Voigt May 02 '11 at 22:42
2

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.

jon hanson
  • 8,722
  • 2
  • 37
  • 61
0

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.

StackedCrooked
  • 34,653
  • 44
  • 154
  • 278
  • 1
    What if its not a double? what if its int/float or string ? As base class is not templatized is it possible ? – Arunmu Apr 30 '11 at 15:48
0

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 :)

vines
  • 5,160
  • 1
  • 27
  • 49
0

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.

iammilind
  • 68,093
  • 33
  • 169
  • 336
0

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.

navigator
  • 1,588
  • 1
  • 13
  • 19