Nested class inside an abstract class
Asked Answered
G

2

6

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.

Glaucous answered 11/3, 2014 at 9:23 Comment(0)
J
5

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...

Jonellejones answered 11/3, 2014 at 9:31 Comment(7)
The problem occurs when you try to create an object of the implementation class B. I have added a main function in the original post for that purpose.Glaucous
@anupamsr Edited the post, check it out now.Jonellejones
Thanks. That almost works. Could you please show me how to add a function in the Iterator class too? I want to use a function which can take an object of type A<T>::Iterator and then use that function on it. Regarding the last part of your comment, I am not sure there will be many use-cases where the abstract class will be able to provide a generic iterator, as it won't have access to the internals of the class (private members).Glaucous
@anupamsr I edited the post. About the generic iterator - you forget about friend classes in C++ ;) Though I wouldn't worry much about that. I implemented my example using pointers; you can use reference instead, but in such case a problem arises with returning iterator from B (B would have to keep instances of iterators in such case and manage their lifetimes).Jonellejones
Any particular reason why I cannot use normal objects instead of pointers/references?Glaucous
@anupamsr Have you actually tried? If A<T>::Iterator is abstract, you won't be able to create a variable to assign B::begin() result to.Jonellejones
Hm... okay. Then this doesn't really solve the problem I am having. Iterator as pointer doesn't fall well into the paradigm. I will check out the concepts.Glaucous
M
2

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.

Mark answered 11/3, 2014 at 10:51 Comment(2)
That is okay, I have implemented operator overloaders in the Iterator class.Glaucous
I will look into concepts because operator overloading (e.g., ==) doesn't work on abstract classes.Glaucous

© 2022 - 2024 — McMap. All rights reserved.