Related
I've tried implementing a list container,
and decided to move some general functions
like sum() to base class, so that I can
reuse them later in other containers.
All the base support class needs are three
methods empty(), head() and tail.
I can't make those pure virtual because support
class will never be instantiated. But it still
has to use those methods to implement its own
methods like sum().
I tried something like this:
#include <iostream>
using namespace std;
template<typename T>
class StatsSupport {
public:
T sum(void) const {
if (empty()) {
return T(0);
} else {
return head() + tail()->sum;
}
}
// other methods
};
template<typename T>
class List : public StatsSupport<T> {
public:
// constructors etc.
bool empty(void) const {return head_ != NULL;}
const T& head(void) const {return *head_;}
const List<T>& tail(void) const {return *tail_;}
// other methods
private:
T* head_;
List<T> *tail_;
};
But trying to use sum() gets me compilation error
prog.cpp:8:13: error: there are no arguments to 'empty' that depend on a template parameter, so a declaration of 'empty' must be available [-fpermissive]
if (empty()) {
^
for each of empty(), head() and tail().
Any advice?
The problem is that StatsSupport cannot find the empty, head etc. functions because these neither exist in its nor in the global scope.
StatsSupport does not know about the functions that exist in the derived class.
Basically there are two ways to solve this:
Runtime polymorphism, where you add a virtual destructor to StatsSupport and add declarations for empty, head etc. which are pure virtual.
Compile time polymorphism via using CRTP as mentioned in the comments.
I will focus on the latter.
So basically StatsSupport needs to get a way to access functions of the derived class.
This can be done by adding the type of the derived class as template parameter, which is called CRTP:
template<class Derived, typename T>
class StatsSupport {
public:
T sum(void) const {
if (derived()->empty()) {
return T(0);
} else {
return derived()->head() + derived()->tail()->sum;
}
}
// other methods
private:
Derived *derived()
{
return static_cast<Derived*>(this);
}
const Derived *derived() const
{
return static_cast<const Derived*>(this);
}
};
template<typename T>
class List : public StatsSupport<List<T>, T> { // with some changes could be simplified to StatsSupport<List<T>> but this it ouf of scope of this question
I am using a function for derived instead of a member to keep the class const correct.
Of course another alternative would be a different design relying on algorithms. There you move sum and all the other functions of StatsSupport into global namesapce and would then access them like sum(my_container_instance).
A more STL like way would be to use iterators. Then you could use std::accumulate to do the summing.
That's a serious design issue: Your StatSupport defines some general functions, but relies on specifics of its child classes.
So when StatSupport gets compiled, it doesn't even know that there is some head() and tail(). That's why you get the error message
Now imagine that one day you want to define other containers that shall inherit from StatSupport, for example your own Vector or Map, or DataBase. These data structures will not have a head and a tail.
Basically there are two main orientations you may take:
define in your StatSupport some virtual functions for iterating through the data structure.
or better, use in your data structures some iterators (like they exist for standard containers) and define some template functions (sum, average, etc...) that use iterators to browse through your container.
In the latter case, you wouldn't need inheritance to benefit from generic functions.
I might miss the point of the question but will give my 5 cents to it anyway :)
The reasoning behind the solution I show below is, that often people new to OOP (in C++) think that they must use inheritance to get things done.
But especially in C++, this is but one way and often not the best way to achieve composition.
While in the majority of cases, the overhead cost of virtual functions does not really matter, the code below shows a way to yield container expansions without using inheritance and without using virtual functions. The weak point of the approach is that the "container function contract" is only implicitly visible.
template <class _X>
class ContainerTypeA < _X >
{
public:
typedef _X value_type;
typedef ContainerTypeA<_X> container_type;
const _X & Head() const
{
// return head of this containers content.
}
container_type Tail() const
{
// return the tail (all elements after the first element in a new instance.
}
bool IsEmpty() const
{
return true; // return whether or not this container is empty.
}
};
template <class _X>
class ContainerTypeB < _X >
{
public:
typedef _X value_type;
typedef ContainerTypeB<_X> container_type;
const _X & Head() const
{
// return head of this containers content.
}
container_type Tail() const
{
// return the tail (all elements after the first element) in a new instance.
}
bool IsEmpty() const
{
return true; // return whether or not this container is empty.
}
};
// Note: In stead of the class with static member functions, this could
// as well be a namespace with template-functions inside.
template < class _ContainerT >
class ContainerStats<_ContainerT>
{
static _ContainerT::value_type Sum(const _ContainerT & container)
{
// Implement sum - possibly in the recursive way you did in your question.
}
// more expansion functions...
};
I get a compile error when I try to do this:
class A
{
virtual std::vector<A*> test() { /* do something */ };
}
class B: public A
{
virtual std::vector<B*> test() { /* do something */ };
}
I assume that A and B are covariant types, and hence A* and B* should also be (Correct?) By inference, I would have expected that std::vector<A*> and std::vector<B*> should be covariant as well, but this does not seem to be the case. Why?
Covariant return types allow overridden virtual member functions in a derived class to return a different type of object, as long as it can be used in all the same ways as the base class's return type. Computer scientists have (ever since Barbara Liskov) a theoretical definition of "can be used in the same ways": substitutability.
No, std::vector<B*> is not a subtype of std::vector<A*>, nor should it be.
For example, std::vector<B*> doesn't support the push_back(A*) operation, so it is not substitutable.
C++ doesn't try to infer subtype relationships for templates at all. The relationship will only exist if you actually specialize one and specify a base class. One reason for this, even on interfaces which are theoretically covariant (basically, read-only), is that C++'s version is actually stronger than Liskov substitution -- in C++ the compatibility has to exist at a binary level. Since the memory layout of collections of related objects may not match subobject placement, this binary compatibility isn't achieved. The restriction of covariant return types to be only pointers or references is also a consequence of the binary compatibility issue. A derived object probably wouldn't fit in the space reserved for the base instance... but its pointer will.
An apple is a fruit.
A bag of apples is not a bag of fruit. That's because you can put a pear in a bag of fruit.
The standard defines covariance for C++ purposes in ยง10.3 [class.virtual]/p7:
The return type of an overriding function shall be either identical to
the return type of the overridden function or covariant with the
classes of the functions. If a function D::f overrides a function
B::f, the return types of the functions are covariant if they
satisfy the following criteria:
both are pointers to classes, both are lvalue references to classes, or both are rvalue references to classes113
the class in the return type of B::f is the same class as the class in the return type of D::f, or is an unambiguous and
accessible direct or indirect base class of the class in the return
type of D::f
both pointers or references have the same cv-qualification and the class type in the return type of D::f has the same cv-qualification
as or less cv-qualification than the class type in the return type of
B::f.
113Multi-level pointers to classes or references to multi-level pointers to classes are not allowed.
Your functions fail on the first point, and, even if you bypass it, fails on the second - std::vector<A*> is not a base of std::vector<B*>.
The C++ FAQ answers this directly in [21.3] Is a parking-lot-of-Car a kind-of parking-lot-of-Vehicle? ("You don't have to like it. But you do have to accept it.")
SO question Getting a vector into a function that expects a vector is asking the same thing. And the answer is that while it seems safe at first to allow covariance of generic types, in particular containers of a derived type being treated as containers of the base type, it is quite unsafe.
Consider this code:
class Vehicle {};
class Car : public Vehicle {};
class Boat : public Vehicle {};
void add_boat(vector<Vehicle*>& vehicles) { vehicles.push_back(new Boat()); }
int main()
{
vector<Car*> cars;
add_boat(cars);
// Uh oh, if that worked we now have a Boat in our Cars vector.
// Fortunately it is not legal to convert vector<Car*> as a vector<Vehicle*> in C++.
}
Templates do not "inherit" covariance, because different template specializations may be completely 100% unrelated:
template<class T> struct MD;
//pets
template<> struct MD<A*>
{
std::string pet_name;
int pet_height;
int pet_weight;
std::string pet_owner;
};
//vehicles
template<> struct MD<B*>
{
virtual ~MD() {}
virtual void fix_motor();
virtual void drive();
virtual bool is_in_the_shop()const;
}
std::vector<MD<A*>> get_pets();
How would you feel if get_pets returned a vector where some of those were actually vehicles instead? It seems to defeat the point of the type system right?
Covariance only happens when you are returning a pointer or a reference to a class, and the classes are related by inheritance.
This is clearly not happening, both because std::vector<?> is not a pointer nor reference, and because two std::vector<?>s have no parent/child relationship.
Now, we can make this work.
Step 1, create an array_view class. It has a begin and end pointer and methods and a size method and all you might expect.
Step 2, create an shared_array_view, which is an array view that also owns a shared_ptr<void> with a custom deleter: it is otherwise identical. This class also insures that the data it is viewing lasts long enough to be viewed.
Step 3, create a range_view, which is a pair of iterators and dressing on it. Do the same with a shared_range_view with an ownership token. Modify your array_view to be a range_view with some extra guarantees (contiguous iterators mainly).
Step 4, write a converting iterator. This is a type that stores an iterator over value_type_1 which either calls a function, or implicitly converts to a const_iterator over value_type_2.
Step 5, Write a range_view< implicit_converting_iterator< T*, U* > > returning function for when T* can be implicitly converted to U*.
Step 6, write type erasers for the above
class A {
owning_array_view<A*> test_() { /* do something */ }
virtual type_erased_range_view<A*> test() { return test_(); };
};
class B: public A {
owning_array_view<B*> test_() { /* do something */ };
virtual type_erased_range_view<A*> test() override {
return convert_range_to<A*>(test_());
}
};
Most of what I describe has been done by boost.
This doesn't work because
you are not returning pointers or references, which is required for covariant returns to work; and
Foo<B> and Foo<B> have no inheritance relationship regardless of Foo, A and B (unless there's a specialization that makes it so).
But we can work around that. First, note that std::vector<A*> and std::vector<B*> are not substitutable for each other, regardless of any language restrictions, simply because std::vector<B*> cannot support adding an A* element to it. So you cannot even write a custom adapter that makes std::vector<B*> a substitute of std::vector<A*>
But a read-only container of B* can be adapted to look like a read-only container of A*. This is a multi-step process.
Create an abstract class template that exports a readonly container-like interface
template <class ApparentElemType>
struct readonly_vector_view_base
{
struct iter
{
virtual std::unique_ptr<iter> clone() const = 0;
virtual ApparentElemType operator*() const = 0;
virtual iter& operator++() = 0;
virtual iter& operator--() = 0;
virtual bool operator== (const iter& other) const = 0;
virtual bool operator!= (const iter& other) const = 0;
virtual ~iter(){}
};
virtual std::unique_ptr<iter> begin() = 0;
virtual std::unique_ptr<iter> end() = 0;
virtual ~readonly_vector_view_base() {}
};
It return pointers to iterators, not iterators themselves, but
don't worry, this class will be only used by an STL-like wrapper anyway.
Now create a concrete wrapper for readonly_vector_view_base and its iterator, so that it contains a pointer to, and delegate its operations to, a readonly_vector_view_base.
template <class ApparentElemType>
class readonly_vector_view
{
public:
readonly_vector_view(const readonly_vector_view& other) : pimpl(other.pimpl) {}
readonly_vector_view(std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl_) : pimpl(pimpl_) {}
typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;
class iter
{
public:
iter(std::unique_ptr<iter_base> it_) : it(it_->clone()) {}
iter(const iter& other) : it(other.it->clone()) {}
iter& operator=(iter& other) { it = other.it->clone(); return *this; }
ApparentElemType operator*() const { return **it; }
iter& operator++() { ++*it; return *this; }
iter& operator--() { --*it; return *this; }
iter operator++(int) { iter n(*this); ++*it; return n; }
iter operator--(int) { iter n(*this); --*it; return n; }
bool operator== (const iter& other) const { return *it == *other.it; }
bool operator!= (const iter& other) const { return *it != *other.it; }
private:
std::unique_ptr<iter_base> it;
};
iter begin() { return iter(pimpl->begin()); }
iter end() { return iter(pimpl->end()); }
private:
std::shared_ptr<readonly_vector_view_base<ApparentElemType>> pimpl;
};
Now create a templatized implementation for readonly_vector_view_base that looks at a vector of a differently typed elements:
template <class ElemType, class ApparentElemType>
struct readonly_vector_view_impl : readonly_vector_view_base<ApparentElemType>
{
typedef typename readonly_vector_view_base<ApparentElemType>::iter iter_base;
readonly_vector_view_impl(std::shared_ptr<std::vector<ElemType>> vec_) : vec(vec_) {}
struct iter : iter_base
{
std::unique_ptr<iter_base> clone() const { std::unique_ptr<iter_base> x(new iter(it)); return x; }
iter(typename std::vector<ElemType>::iterator it_) : it(it_) {}
ApparentElemType operator*() const { return *it; }
iter& operator++() { ++it; return *this; }
iter& operator--() { ++it; return *this; }
bool operator== (const iter_base& other) const {
const iter* real_other = dynamic_cast<const iter*>(&other);
return (real_other && it == real_other->it);
}
bool operator!= (const iter_base& other) const { return ! (*this == other); }
typename std::vector<ElemType>::iterator it;
};
std::unique_ptr<iter_base> begin() {
iter* x (new iter(vec->begin()));
std::unique_ptr<iter_base> y(x);
return y;
}
std::unique_ptr<iter_base> end() {
iter* x (new iter(vec->end()));;
std::unique_ptr<iter_base> y(x);
return y;
}
std::shared_ptr<std::vector<ElemType>> vec;
};
OK, as long as we have two types where one is convertible to another, such as A* and B*, we can view a vector of B* as if it's a vector of A*.
But what does it buy us? readonly_vector_view<A*> is still unrelated to readonly_vector_view<B*>! Read on...
It turns out that the covariant return types are not really necessary, they are a syntactic sugar to what is available in C++ otherwise. Suppose C++ doesn't have covariant return types, can we simulate them? It's actually pretty easy:
class Base
{
virtual Base* clone_Base() { ... actual impl ... }
Base* clone() { return clone_Base(); } // note not virtual
};
class Derived : public Base
{
virtual Derived* clone_Derived() { ... actual impl ... }
virtual Base* clone_Base() { return clone_Derived(); }
Derived* clone() { return clone_Derived(); } // note not virtual
};
It's actually pretty easy and there's no requirement for the return type to be pointers or references, or have an inheritance relationship. It is enough that there is a conversion:
class Base
{
virtual shared_ptr<Base> clone_Base() { ... actual impl ... }
shared_ptr<Base> clone() { return clone_Base(); }
};
class Derived : public Base
{
virtual shared_ptr<Derived> clone_Derived() { ... actual impl ... }
virtual shared_ptr<Base> clone_Base() { return clone_Derived(); }
shared_ptr<Derived> clone() { return clone_Derived(); }
};
In a similar fashion, we can arrange A::test() to return a readonly_vector_view<A*>, and B::test() to return a readonly_vector_view<B*>. Since these functions are now not virtual, there is no requirement for their return types to be in any relationship. One just hides the other. But inside they call a virtual function that creates (say) a readonly_vector_view<A*> implemented in terms of readonly_vector_view_impl<B*, A*> which is implemented in terms of vector<B*>, and everything works just as if they were real covariant return types.
struct A
{
readonly_vector_view<A*> test() { return test_A(); }
virtual readonly_vector_view<A*> test_A() = 0;
};
struct B : A
{
std::shared_ptr<std::vector<B*>> bvec;
readonly_vector_view<B*> test() { return test_B(); }
virtual readonly_vector_view<A*> test_A() {
return readonly_vector_view<A*>(std::make_shared<readonly_vector_view_impl<B*, A*>>(bvec));
}
virtual readonly_vector_view<B*> test_B() {
return readonly_vector_view<B*>(std::make_shared<readonly_vector_view_impl<B*, B*>>(bvec));
}
};
Piece of cake! Live demo Totally worth the effort!
The constructor of my class can have two different classes passed to it. But there's no way I can know ahead of time what type it will be so I can declare its type as a data member to initialize it. How can I know which kind of object was passed to my constructor. Preferably I would like this to be done without Boost.
I am passing in an iterator. It can either be a const iterator (v.cbegin()) or non-const (v.begin()):
struct iterator
{
iterator(IteratorType it)
: m_it(it)
{ }
IteratorType m_it;
};
Here's how I'm calling the constructor:
iterator X::begin() { return iterator(x.begin()); }
iterator X::begin() const { return iterator(x.cbegin()); }
You do not actually want to know what argument-types were passed to your ctor, because by then the class is already under construction, its type cast in stone.
What you want is to decide on which class to use based on the arguments, which needs earlier intervention.
Still, the solution is simple:
Use a factory-function and a template-class (There are many examples of this pattern in <iterator> and other parts of the standard library)
template<class It>
struct myiterator
{
myiterator(It it) : m_it(it) { }
private:
typename std::iterator_traits<It>::type m_it;
// The above will choke if It is not an iterator
};
template<class... X> auto make_myiterator(X&&.. x)
-> myiterator<typename std::decay<X>::type>
// Only the first arguments type is passed on.
{
using just_making_sure_it_is_an_iterator =
std::iterator_traits<typename std::decay<X>::type>::type;
return {std::forward<X>(x)...};
}
I solved this by using templates as suggested in the comments:
template<typename It>
struct iterator
{
iterator(It it) : m_it(it) { }
private:
It m_it;
};
If your target is to determine which type you are holding in your class, by just holding a more "general" type, then you could do this:
Implement a common interface class of those two classes and use this type as your constructor argument & property. Inside your interface, set up a method contract, that returns your type (i.e. a string or enum value, etc). Then, implement this method to both classes, in different implementation.
public class Iterateable
{
public:
//Ctors/Dtors etc....
//Pure virtual method.
virtual string getType() = 0;
}
public class IteratorType1 : public Iterateable
{
public:
//Ctors/Dtors etc....
string getType {return "IteratorType1";}
}
public class IteratorType2 : public Iterateable
{
public:
//Ctors/Dtors etc....
string getType {return "IteratorType2";}
}
struct iterator
{
iterator(Iterateable it)
: m_it(it)
{ }
Iterateable m_it;
};
You can then hold objects of any type that implement the Iterateable interface.
Then, by calling getType() via the property you can determine the type, as the method implemented on the mostly derived class of the instance holded on the property will be invoked (this is a language feature called late binding).
I am trying to make 3 iterators (3 classes) for a binary tree (one for each traversal) and I want them to share the same base class.
class BinaryTree
{
class Iterator
{
virtual Iterator operator++() = 0;
}
class Iterator1 : public Iterator
{
}
class Iterator2 : public Iterator
{
}
class Iterator3 : public Iterator
{
}
}
I have implemented the methods that they share inside the Iterator class. The only methods that differ are the constructors and the overloading of the prefixed ++.
My idea was to make the overloaded operator ++ a virtual function so that it can be overridden by Iterator1, 2 and 3, because I would like to use the iterator like this:
for(BinaryTree::Iterator it = t.begin(Preorder); it != t.end(); it++)
{
// do stuff
}
I was used to Java, where you could make an object of an abstract type but instantiate it as another type.
The problem is that in C++ it cannot allocate an object of abstract type.
The begin method is made using the factory pattern: it returns an object of Iterator 1, 2 or 3 type.
Is there any way around this in order to keep the same syntax as in the for?
Remember, in Java, you don't deal directly with objects, you have pointers to objects. In C++, when you declare a variable of type x, you have an x, not something that derives from x. If you want to use polymorphism, you need either a reference (x&) or a pointer (x*, shared_ptr<x>, unique_ptr<x>, ...).
So, your for loop should be
for(BinaryTree::Iterator& it = t.begin_preorder(); it != t.end; it++)
And BinaryTree::begin_preorder() should return a BinaryTree::Iterator#, where # is the number that corresponds to the correct iterator. It can't return a BinaryTree::Iterator, as you cannot have objects of that type (it's abstract). If it returns a BinaryTree::Iterator&, then you won't be able to return a (local) object that was created within the function, as the object will no longer exist once the function exits. If you return a reference, the actual object the reference points to would have to be cached inside the t object.
I assume you want to use polymorphism because you don't necessarily know at compile time which iterator you're going to use (since if you do know at compile time, polymorphism is unnecessary).
If you just want to save some typing in your for loop and don't need polymorphism:
for(auto it = t.begin_preorder(); it != t.end; it++)
will work fine. Then you don't need the base class or virtual member functions.
You need to use pointers or references with polymorphic types:
struct Test {
struct Iterator {
virtual Iterator &operator++() = 0;
virtual ~Iterator() = 0;
};
struct Forward : public Iterator {
int i;
virtual Iterator &operator++()
{
i++;
return *this;
}
};
Iterator *forward()
{
return new Forward();
}
};
int main()
{
Test t;
Test::Iterator &it = *t.forward();
++it;
delete ⁢
}
Getting these things to work more like regular iterators can be done by hiding the polymorphism behind a non-polymorphic wrapper class (the pimpl, or handle-body idiom):
class IteratorImpl {
virtual IteratorImpl *clone() = 0;
virtual void increment() = 0;
virtual ~IteratorImpl() = 0;
};
// concrete subclasses
class iterator {
std::unique_ptr<IteratorImpl> impl;
public:
// operator++() based on IteratorImpl::increment
// copy constructor based on IteratorImpl::clone
};
I have a particular class that stores a piece of data, which implements an interface:
template<typename T>
class MyContainer : public Container<T> {
class Something : public IInterface {
public:
// implement *, ->, and ++ here but how?
private:
T x;
};
// implement begin and end here, but how?
private:
Something* data; // data holds the array of Somethings so that references to them can be returned from begin() and end() to items in this array so the interface will work, but this makes the problem described below
};
And I have an array of Somethings.
I have the need for Something to implement an interface class (IInterface in the example) which:
Contains pure virtual member functions which return something such that *retval returns a reference to the x member, retval-> returns the address of x, and ++retval makes retval refer to the next Something in the array.
The things that the pure virtual members return can be inherited from and returned by the implementation of the members
container[i] (where container is the array holding the Something objects) always returns something such that *retval always returns a reference to the same T for the same i.
Right now, the interface looks like this:
template<typename T>
class Container {
class IInterface {
public:
virtual T& operator*() = 0;
virtual T* operator->() = 0;
virtual IInterface& operator++(); // this is the problem
};
// returning a reference right now to support covariance, so subclasses can
// derive from Container and then have a member class derive from IInterface
// and override these to return their derived class, but this has a problem
virtual IInterface& begin() = 0;
virtual IInterface& end() = 0;
};
My current solution (have the virtual methods return an IInterface& and return a Something& in the implementation) has no problem with the requirements, except for the ++retval requirement. Because the Something is directly tied to the object it holds and can't point to a T with a pointer, there's no way that I can find to get ++ to make the variable refer to the next Something in the array.
If it helps to know, this is an iterator type system. I would have made it with the STL style iterators (where you just have an array of T) that are passed around by value and hold pointers to the values they represent, but that would break the interface because only references and pointers are covariant, and the objects already have to exist somewhere else already (in my code they're in the array) so you don't return a reference to a local object.
The purpose of this setup is so that one can write functions that take a Container& and iterate the container without knowing what type of container it is:
void iterate(Container<int>& somecontainer) {
Container<int>::IIterator i = somecontainer.begin(); // right now this would return a reference, but it doesn't/can't work that way
while (i != somecontainer.end()) {
doSomething(*i);
++i; // this is the problem
}
}
It's kind of difficult for me to describe, don't hesitate to let me know if you need more information.
What you are trying to do is called type erasure. Basically you want to provide a value type (which is the same across the whole inheritance hierarchy) that wraps the particular iterator type and offers a uniform dynamic interface.
Type erasure is usually implemented with a non-virtual class (the type erased) that stores a pointer to a virtual base class that implements the erasure, from which you derive different types that wrap each particular iterator. The static class would offer templated constructor/assignment operators that would dynamically instantiate an object of the derived type and store the pointer internally. Then you only need to implement the set of operations as dispatch to the internal object.
For the simplest form of type erasure possible, you can take a look at the implementation of boost::any (documentation is here)
Sketch:
namespace detail {
template<typename T>
struct any_iterator_base {
virtual T* operator->() = 0; // Correct implementation of operator-> is tough!
virtual T& operator*() = 0;
virtual any_iterator_base& operator++() = 0;
};
template <typename T, typename Iterator>
class any_iterator_impl : any_iterator_base {
Iterator it;
public:
any_iterator_impl( Iterator it ) : it(it) {}
virtual T& operator*() {
return *it;
}
any_iterator_impl& operator++() {
++it;
return *this;
}
};
}
template <typename T>
class any_iterator {
detail::any_iterator_base<T>* it;
public:
template <typename Iterator>
any_iterator( Iterator it ) : it( new detail::any_iterator_impl<T,Iterator>(it) ) {}
~any_iterator() {
delete it;
}
// implement other constructors, including copy construction
// implement assignment!!! (Rule of the Three)
T& operator*() {
return *it; // virtual dispatch
}
};
The actual implementation becomes really messy. You need to provide different versions of the iterator for the different iterator types in the standard, and the detail of the implementation of the operators might not be trivial either. In particular operator-> is applied iteratively until a raw pointer is obtained, and you want to make sure that your type erased behavior does not break that invariant or document how you break it (i.e. limitations on the type T that your adaptor can wrap)
For extended reading:
- On the Tension Between Object-Oriented and Generic Programming in C++
- any_iterator: Implementing Erasure for C++ iterators
- adobe any_iterator ,
I would suggest a look at the Visitor pattern.
Other than that, what you want is a value type that will be imbued with polymorphic behavior. There is a much simpler solution than James' using your IInterface.
class IInterface
{
virtual ~IInterface() {}
virtual void next() = 0;
virtual void previous() = 0;
virtual T* pointer() const = 0;
virtual std::unique_ptr<IInterface> clone() const = 0;
};
std::unique_ptr<IInterface> clone(std::unique_ptr<IInterface> const& rhs) {
if (!rhs) { return std::unique_ptr<IInterface>(); }
return rhs->clone();
}
class Iterator
{
friend class Container;
public:
Iterator(): _impl() {}
// Implement deep copy
Iterator(Iterator const& rhs): _impl(clone(rhs._impl)) {}
Iterator& operator=(Iterator rhs) { swap(*this, rhs); return *this; }
friend void swap(Iterator& lhs, Iterator& rhs) {
swap(lhs._impl, rhs._impl);
}
Iterator& operator++() { assert(_impl); _impl->next(); return *this; }
Iterator& operator--() { assert(_impl); _impl->previous(); return *this; }
Iterator operator++(int); // usual
Iterator operator--(int); // usual
T* operator->() const { assert(_impl); return _impl->pointer(); }
T& operator*() const { assert(_impl); return *_impl->pointer(); }
private:
Iterator(std::unique_ptr<IInterface> impl): _impl(impl) {}
std::unique_ptr<IInterface> _impl;
};
And finally, the Container class will propose:
protected:
virtual std::unique_ptr<IInterface> make_begin() = 0;
virtual std::unique_ptr<IInterface> make_end() = 0;
And implement:
public:
Iterator begin() { return Iterator(make_begin()); }
Iteraotr end() { return Iterator(make_end()); }
Note:
You can do away with the std::unique_ptr if you can avoid the ownership issue. If you can restrict the IInterface to be behavioral only (by extracting the state into Iterator), then you can have the Strategy pattern kick-in, and use a pointer a statically allocated object. This way, you avoid dynamic allocation of memory.
Of course, it means your iterators won't be so rich, as it requires IInterface implementations to be stateless, and implementing "filtering" iterators, for example, would become impossible.
Have you thought about using CRTP. I find it a good candidate here. Here is a brief demo. It just explains your ++retval problem (if I understood it correctly). You have to change your IInterface definition from pure virtual to CRTP type interface.
template<class Derived>
struct IInterface
{
Derived& operator ++ ()
{
return ++ *(static_cast<Derived*>(this));
}
};
struct Something : public IInterface<Something>
{
int x;
Something& operator ++ ()
{
++x;
return *this;
}
};
There are some limitations of CRTP, that the template will always follow your IInterface. Which means that if you are passing a Something object to a function like this:
foo(new Something);
Then, foo() should be defined as:
template<typename T>
void foo(IInterface<T> *p)
{
//...
++(*p);
}
However for your problem, it can be a good fit.
Like you said, the problem is that instances of Something are tied to the object it holds. So let's try to untie them.
The key point to remember is that in OOP, public non-const data members are generally frowned upon. In your current implementation, every Something instance is tied to having a data member T x which is publicly accessible. Instead of this, is considered better to make an abstraction of this, i.e. provide accessor methods instead:
class Something : IInterface
{
private:
T x;
public:
T GetX()
{
return x;
}
};
Now the user has know idea what type of thing x is, much less that x exists.
This is a good first step, however, since you wish be able to have x refer to different objects at different times, we're pretty much going to have to make x be a pointer. And as a concession to conventional code, we'll also make GetX() return a const reference, rather than a regular value:
class Something: IInterface
{
private:
T *x;
public:
T const& GetX()
{
return *x;
}
};
It's now trivial to implement the methods in IInterface:
class Something: IInterface
{
private:
T *x;
public:
T const& GetX()
{
return *x;
}
T& operator*()
{
return *x;
}
T* operator->()
{
return x;
}
Something& operator++()
{
++x;
return *this;
}
};
The ++ operator is trivial now - it really just applies the ++ to x.
The user now has no idea that a pointer was used. All they know is that their code works right. That's the most important point in OOP's principle of data abstraction.
Edit
As far as implementing the begin and end methods of Container, that shouldn't be too difficult either, but it will require some changes to Container.
First off, let's add a private constructor to Something which takes a pointer to the starting object. We'll also make MyContainer a friend of Something:
class Something: IInterface
{
friend class MyContainer; // Can't test the code right now - may need to be MyContainer<T> or ::MyContainer<T> or something.
private:
T *x;
Something( T * first )
: x(first)
{
}
public:
T const& GetX()
{
return *x;
}
T& operator*()
{
return *x;
}
T* operator->()
{
return x;
}
Something& operator++()
{
++x;
return *this;
}
};
By making the constructor private, and setting the friend dependancy, we ensure that only MyContainer can make new Something iterators (this protects us iterating over random memory if something erroneous were given by a user).
Next off, we'll change MyContainer a little, so that rather than having an array of Something, we'll just have an array of T:
class MyContainer
{
...
private:
T *data;
};
Before we get to implementing begin and end, let's make that change to Container I talked about:
template<typename T, typename IteratorType>
class Container {
public:
...
// These prototype are the key. Notice the return type is IteratorType (value, not reference)
virtual IteratorType begin() = 0;
virtual IteratorType end() = 0;
};
So rather than relying on covariance (which would be really difficult in this case), we use a little template magic to do what we want.
Of course, since Container now accepts another type parameter, we need a corresponding change to MyContainer; namely we need to provide Something as the type parameter to Container:
template<class T>
class MyContainer : Container<T, Something>
...
And the begin/end methods are now easy:
template<class T>
MyContainer<T>::begin()
{
return Something(data);
}
template<class T>
MyContainer<T>::end()
{
// this part depends on your implementation of MyContainer.
// I'll just assume your have a length field in MyContainer.
return Something(data + length);
}
So this is what I've got for my midnight thinking. Like I mentioned above, I cannot currently test this code, so you might have to tweak it a bit. Hopefully this does what you want.
If the usage is supposed to be similar to stdlib, then the iterator needs to be a value object, because it normally gets copied by value a lot. (Also, otherwise what would the begin and end method return a reference to?)
template <class T>
class Iterator
{
shared_ptr<IIterator> it;
public:
Iterator(shared_ptr<IIterator>);
T& operator*() { it->deref(); }
T* operator->() { return &it->deref(); }
Iterator& operator++() { it->inc(); return *this; }
etc.
};