Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested class inside an abstract class

I have an abstract class like so:

class A
{
public:
    void func() = 0;
};

Can I force its implementations to have a nested iterator class too?

#include <iterator>

template<typename T>
class A
{
public:
    class Iterator : public std::iterator<std::forward_iterator_tag, T>
    {
    };

    virtual Iterator begin() const = 0;
    virtual void func() = 0;
};

template<typename T>
class B : public A<T>
{
public:
    B() {}
    class Iterator : public std::iterator<std::forward_iterator_tag, T>
    {
    };

    Iterator begin() const
    {
        return Iterator();
    }

    virtual void func()
    {
    }
};

int main()
{
    B<int> b;
}

I just want to know if this is possible, and if it is, what am I missing? Since the iterator class will depend on how the class A is implemented, I don't know if a formal implementation is possible.

like image 603
Anupam Srivastava Avatar asked Mar 11 '14 09:03

Anupam Srivastava


2 Answers

Try this one:

template<typename T>
class A
{
public:
    class Iterator : public std::iterator<std::forward_iterator_tag, T>
    {
    public:
        virtual void DoSomething() = 0;
    };

    virtual Iterator * begin() const = 0;
    virtual void func() = 0;
};

template<typename T>
class B : public A<T>
{
public:
    B() {}
    class BIterator : public A<T>::Iterator
    {
    public:
        void DoSomething()
        {
            std::cout << "Hello world from B::BIterator!";
        }
    };

    A<T>::Iterator * begin() const
    {
        return new BIterator();
    }

    virtual void func()
    {
    }
};

int main(int argc, char * argv[])
{
    B<int> b;

    A<int>::Iterator * iter = b.begin();
    iter->DoSomething();
    delete iter;

    getchar();
}

Note, that this won't force B to implement a new instance of A<T>::Iterator, but simply to provide one. Developer may, for instance, implement a single generic iterator and return it from all A<T>-derived classes. But I guess, that should be enough, shouldn't it?


Edit: In response to comments

You should return the iterator by pointer. Since you want it to be an abstract class, you cannot declare instance of it like:

A<int>::Iterator iter = b.begin(); // Error: cannot instantiate abstract class
                                   // A<int>::Iterator

You can do it however if you resign from A<T>::Iterator being an abstract class. But then you won't be able to force one to descend from A<T>::Iterator...

like image 149
Spook Avatar answered Sep 21 '22 05:09

Spook


I just want to know if this is possible, and if it is, what am I missing?

Well, no, unless you define at least a pure virtual member function for Iterator you cannot force derived class to have one.

The way the standard library solves this is to use concepts. They are constraints on template argument which in turn define common interfaces for common objects.

Take for example the Container concept: it requires a generic type T to have the following valid expressions:

  • ...
  • T::reference
  • T::const_reference
  • T::iterator
  • ...

In this way, you could define a function:

template<class Container>
void do_something(Container c) {
    Container::iterator it = c.begin();
    // etc
}

and expect it to work with every type that fit the Container concept. In fact, the above code happily compiles using it with 3 different standard containers (namely vector, list and deque).

In this way you, as a C++ user, can create your own containers, make it fit the requirements for concept you want to support and be able to use all the algorithms that any other containers have, automatically. No interface required, no public inheritance, nothing.

Not to mention that an explicit Concept Lite proposal is being discussed these days as to implement these "interfaces" in a more explicit manner.

like image 20
Shoe Avatar answered Sep 21 '22 05:09

Shoe