-5

Edited Shorter Question: When using a pointer to an abstract class, how can I call methods of the actual type of the object it points to?

I have an abstract BaseTree class with a public

virtual BaseTree const& operator[](NodeIndex const& poss) const = 0;

where NodeIndex is in practice std::vector<unsigned int>. I have also a TreeBasicIterator class which includes

public:
    /// Access the tree to which m_tree points
    BaseTree& operator*() const
    {
        //checking...
        return (*m_tree)[*this];
    }

private:
    /// the tree the current iterator refers to
    BaseTree* m_tree=nullptr;

Problem (I think it is the crux of my initial question below):

How can I use the methods of the class corresponding to the actual type of *m_tree? For now, when dereferencing my iterator, I can only use methods of the abstract class BaseTree.

I would like to avoid making TreeBasicIterator a template (see the initial question below).

Initial Question

As many (relatively new?) users of C++, I would like to write

virtual typedef something my_virtual_type;

inside classes definitions. Unfortunately it is not possible...

The most popular alternative seems to be templates, as for instance in alternative to virtual typedef which provides a nice sum up.

However I am not sure it covers everything a virtual typedef would. I present below a problem, for which I would like to have virtual typedefs. I have a (temporary?) working solution using overloading and not templates.

I want to create a fancy tree structure, including iterators.

1) I define a NodeIndex type, ancestor of my future iterators. (NodeIndex is in my case std::vector<unsigned int>, but it doesn't matter.)

2) I define an abstract BaseTree class, used in my iterators bellow. The class includes in particular virtual methods which returns NodeIndex indexes to move in the tree (e.g. NodeIndex begin()), and operator[](NodeIndex const&).

3) I define a TreeBasicIterator class, inheriting from NodeIndex, which for coherence purpose includes a const reference to a BaseTree, and implements various iterators methods (making use of the virtual methods provided by BaseTree).

(I have actually two classes: TreeBasicIterator, and Const_TreeBasicIterator with a const reference to a const BaseTree; I use preprocessor macros to mimic a template based on const-ness, but this is another problem.)

4) I have defined a TreeNode<T> template class (T is the node content), inheriting from BaseTree. For now its begin() method returns a NodeIndex, and its operator[] takes a NodeIndex as argument, and everything works fine.

5) However I want to include my TreeBasicIterator in my TreeNode class. I would like to include a

virtual typedef NodeIndex Iterator;

in my BaseTree class, and modify the virtual begin() method signature to Iterator begin(). Then TreeNode would include a

virtual typedef TreeBasicIterator Iterator;

its begin() method signature would be Iterator begin(), and everything would be hopefully fine :)

6) I do not wish to use templates on TreeBasicIterator: it is now compiled once for all.

Making BaseTree a template would make TreeBasicIterator a template, and so loose a part of the benefits of the abstraction in BaseTree.

Instanciating BaseTree with NodeIndex as Iterator type would be superfluous: in practice nobody would inherit from that particular instance.

7) Prospective:

I will use trees with a fixed or rarely modified structure, but more frequently modified node contents, and even much more frequently read accesses. To optimize access, I intend to further derive from TreeNode a TreeVector class, which will include a sorted exhaustive std::vector of NodeIndex (or BasicTreeIterator?) indexes. The Iterator virtual type in TreeVector would be std::iterator.

8) Current (temporary?) partially working solution:

TreeNode defines (via a usual typedef) an Iterator type (my TreeBasicIterator class). It does not override an Iterator type from BaseTree, but it could.

BaseTree has a NodeIndex begin() const method, which is both un-hidden, and overloaded in TreeNode by a Iterator begin() const method. It is not overridden.

Similarly, I have a NodeIndex next_index(NodeIndex) const in BaseTree, which is both un-hidden, and overloaded in TreeNode by a Iterator next_index(Iterator) const method.

Community
  • 1
  • 1
dominique
  • 1
  • 3

1 Answers1

0

So, It does not seem to interest anyone... Anyway, here my working solution: mixing template and non template classes.

TreeBasicIterator remains a non-template class as explained in the question.

dynamic_cast is the tool to call tree class-specific methods on its BaseTree pointer. However it is not reasonnable to have to use it each time an iterator is dereferenced in the code.

I derive a template TreeIterator class:

template<class MyTree> class TreeIterator: public TreeBasicIterator

whose methods use dynamic_cast, for instance:

TreeIterator(BaseTree & tree): TreeBasicIterator(tree) {check_type();};

MyTree& operator*() const
{
    return dynamic_cast<MyTree &>(TreeBasicIterator::operator*());
}

void check_type()
{
    try
    {
        // uses the built-in checkings of dynamic_cast
        if(m_tree!=nullptr) dynamic_cast<MyTree &>(*m_tree);
    }
    catch(std::exception &e)
    {
        m_tree=nullptr; // invalidates the iterator
        throw e;
    }
}

And the once-for-all compiled methods of TreeBasicIterator are called when what is done is independent of the real tree class used.

dominique
  • 1
  • 3