views:

254

answers:

8

How do I get around this? I clearly cannot make the value() method virtual as I won't know what type it is beforehand, and may not know this when accessing the method from b:

class Base
{
public:
    Base() { }
    virtual ~Base() { }
private:
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base
{
public:
    Derived(T value) : m_value(value) { }
    ~Derived() { }

    T value() { return m_value; }
    void setValue(T value) { m_value = value; }
private:
    T m_value;
};

int main()
{
    Base* b = new Derived<int>(5);

    int v = b->value();

    return 0;
}

Compilation errors:

error: 'class Base' has no member named 'value'
A: 

In this case Base will need to know about the template type so b->value returns a T.

I would suggest adding the template to Base and then make value a virtual function on Base

Mark
In actual fact Base represents a lot of classes and a lot of implementation that I didn't want to make templated to prevent my compile time increasing significantly. Ideally I'd like to keep Base un-templated if possible.
Dan
A: 

You could cast your pointer to Derived:

int v = dynamic_cast<Derived<int>*>(b)->value();

Of course in real code, you have to add checking to make sure that dynamic_cast didn't fail.

AraK
A: 

Why don't make Base template-based class too? Then you can have value as a virtual member.

Or you can downcast b to Derived.

Ashalynd
+2  A: 

This statement:

int v = b->value();

The variable 'b' is being trated like it is an object of Derived<int>.
So tell the compiler:

int v = dynamic_cast<Derived<int>*>(b)->value();

Note: If b is not a Derived<int> the result of the cast is NULL.
So the following would probably be safer:

Derived<int>*  d = dynamic_cast<Derived<int>*>(b);
if (d)
{
    int v = d->value();
}
else
{
    // Error
}

Alternatively by using references you get a bad_cast exception thrown:

// Throw bad_cast on failure.
Derived<int>& d = dynamic_cast<Derived<int>&>(*b);
int v = d->value();

Or to be nice and obscrure we can do it one line.

// Assign v or throw bad_cast exception:
int v = dynamic_cast<Derived<int>&>(*b).value();

But I think you can achieve what you are trying to do with the boost::any

int main()
{
    boost::any   b(5);

    int    v = boost::any_cast<int>(b);

    b        = 5.6; // double
    double d = boost::any_cast<double>(b);
}
Martin York
This will probably be the safest way of tackling it. Am just looking at the possibility of adding a templated 'value' function to my base class to perform the casting and type checking, then call the derived value function from this.
Dan
Sounds like you are trying to omplement the boost::any class.
Martin York
+2  A: 

I think there may be some problems with your design if you ask this question (from my experience, with my designs).
However, there are some workarounds:

  1. As other people already answered, you can make Base a class template.
  2. You can downcast b (although I prefer to avoid downcasts if I can)
  3. You can declare value() as returning boost::any in Base and make it pure virtual
  4. You can even declare it as returning void* (but don't do that. It's an option, but a bad one)

But the real question here is what do you try to describe here? What is the meaning of Base, Derived and value(). Ask yourself these question and you may not need these answers...

Oren S
+1  A: 

Some solution:

template < typename T>
class Base{
public: 
    Base() { }    
    virtual ~Base() { }
    virtual T value() = 0;
private:    
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base<T> {
  ...
}

int main(){    
    Base<int>* b = new Derived<int>(5);    
    int v = b->value();    
    return 0;
}

Another solution:

class Base {
public: 
    Base() { }    
    virtual ~Base() { }
    template<class T> T value() const;
private:    
    int m_anotherVariable;
};

template <typename T>
class Base2 : public Base {
public: 
    Base2() { }    
    virtual ~Base2() { }
    virtual T getValue() const = 0;    
};

template<class T> T Base::value() const {
    const Base2<T> * d = dynamic_cast<const Base2<T> *>(this);
    return d ? d->getvalue() : T();
}

template <typename T>
class Derived : public Base2<T> {
public:    
    Derived(T value) : m_value(value) { }    
    virtual ~Derived() { }    
    void setValue(T value) { m_value = value; }
    virtual T getValue() const { return  m_value; }    
private:    
     T m_value;
}

int main(){    
    Base* b = new Derived<int>(5);    
    int v = b->value<int>();
    return 0;
}
Alexey Malistov
A: 

It looks like you want dynamic polymorphism but using only the "static polymorphism" of templates.

If you want dynamic polymorphism you do need virtual members in your base class ( along with your virtual destructor ) or the down_cast if you do not have a common interface.

If not, remove the virtual destructor and use only pointers to or instances of derived types.

About Base as a template :

It will prevent you from having a single base class for dynamic polymorphism as Base< int > and Base< other > will be incompatibles.

fa.
A: 

One way of handling this is via the visitor pattern. The basic idea is that in your hierarchy of classes you implement an onNode function that accepts a visitor. Then, you write specific visitors that do what you want. In your case, you'd end up with:

class GetIntValue : public BaseVisitor
{
public:
  GetIntValue (int & result)
  : m_result (result) {}

  void onNode (Derived<int> & d)
  {
    m_result = d.m_value;
  }
  void onNode (Derived<A> & d)
  {
    assert (! "calling GetIntValue on a node that doesn't have a value");
  }


private:
  int & m_result;
};

int main()
{
  Base* b = new Derived<int>(5);

  int v;
  GetIntValue(v).visit (*b);

  return 0;
}
Richard Corden