MichaelMitchell
MichaelMitchell

Reputation: 1167

C++ Polymorphism with types

I am trying to have a Node which has a variable with type T, T data; along with storing pointers to its parent node NodeBase *parent;.

The classes look as such:

class Node: public NodeBase {
    T data;
    NodeBase *parent;
public:
    Node(T);
    void setData(T);
    T * getData(void);
    void setParent(NodeBase * const);
    NodeBase * getParent(void);
};

class NodeBase {
};

I thought that I could put a pure virtual function in NodeBase, but you have to specify the return type, and since NodeBase does not have a type I cannot specify virtual T * getData(void) = 0;

The specific case that I am having issues with:

Node<char> n1 = Node('A');
Node<int>  n2 = Node(63);
n1.setParent(&n2);

NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

Upvotes: 1

Views: 213

Answers (4)

Paweł Stawarz
Paweł Stawarz

Reputation: 4012

You could add a templated function to NodeBase:

class NodeBase {
    public:
       template<typename T>
       T* getData();
};

Which would cast the object into Node<T> and return getData(). Here's a working example - included only cause it actually took me a while to fix problems with cyclic definitions:

class NodeBase {
    public:
    template<typename T>
    T* getData();
};

template<typename T>
class Node: public NodeBase {
    T data;
    NodeBase *parent;

protected:  
    friend NodeBase;
    T * getData(void){return &data;};

public:
    Node(T var){data=var;};
    void setData(T& var){data=var;};
    void setParent(NodeBase * const par){parent=par;};
    NodeBase * getParent(void){return parent;};
};

template<typename T>
T* NodeBase::getData(){ return dynamic_cast<Node<T>*>(this)->getData();};


int main(){
    Node<char> n1 = Node<char>('A');
    Node<int>  n2 = Node<int>(63);
    n1.setParent(&n2);

    NodeBase *pNode = n1.getParent();
    std::cout << *pNode->getData<int>() << std::endl;

    return 0;
}

Upvotes: 2

user3125280
user3125280

Reputation: 2829

C++ is statically typed, so everytime you write code like this:

NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

the compiler needs to know the type of getData(). In your case, you want it to be either int(void) or char(void) - functions with no arguments returning char or int.

There are ways around this. The strategy will depend on your situation. There are a few cases for the return type:

  • A fixed set of bultins - use a union
  • A fix set of builtins or classes, but the type will never be changed - use a union
  • A fix set of builtins or classes, but the type may change - use boost variant
  • Any other case - use boost any

So in summary, add a virtual function to the base class (non-templated) that returns one of the above data structures. The documentation for boost is easy to find online.

The reason why this is so much more difficult in c++ is because other languages (especially dynamic languages) hide the difficulty. When a function call is made, space is allocated - typically on the stack for the best speed - and the return value is copied into that space. If you want to return an unknown type you need to either make the maximum space available of all possible return types (a union, maybe boost variant?) or allocate that space from the heap (boost any). Many statically typed languages provide good support only for the former.

C++ does have some helpful features if you want to do it yourself.

#include <iostream>
#include <typeinfo>

using namespace std;

struct NodeBase {
    virtual ~NodeBase(){}
};

template<typename T>
struct Node: public NodeBase {
    T data;
    NodeBase *parent;

    Node(T data, NodeBase* parent = nullptr)
        : data(data), parent(parent)
    {}

    ~Node(){}
};

int main()
{
    Node<int>  n2(63);
    Node<char> n1('A', &n2);

    NodeBase* b = n1.parent; //pointer to n2

    if(typeid(*b) == typeid(Node<int>))
        cout << ((Node<int>*) b)->data << endl;

    return 0;
}

Note that typeinfo and at least one virtual function in NodeBase are required for this to work.

Upvotes: 2

Code-Apprentice
Code-Apprentice

Reputation: 83527

I believe you are confusing run-time polymorphism with compile-time polymorphism. Templates provide the later. One possible solution to your problem is to make NodeBase a template class:

template <class T>
class NodeBase {
    public:
        virtual T getData() = 0;
};

template <class T>
class Node : NodeBase<T> {
    T data;
    NodeBase *parent;

public:
    Node(T);
    void setData(T);
    T getData(void);
    void setParent(NodeBase<T> * const);
    NodeBase<T> * getParent(void);
};

Note that I made getData return T rather than T*. (Is there a reason to return a pointer?)

Upvotes: 2

Pranit Kothari
Pranit Kothari

Reputation: 9841

NodeBase *pNode = n1.getParent();
pNode->getData(); // Error: BaseNode has no member 'getData()'

You have pNode (child), so you can use directly to getData(), secondly your base class know nothing about getData(), so using upcasted pointer you cannot call it.

Upvotes: -1

Related Questions