views:

366

answers:

5

Hi all.

I've got a class :

class base
{
public :
 base & operator +=(const int value) = 0;
 // base operator + (const int val) = 0; // HOW DO I DO THIS ?
};

And a child class that derives from it

class derived : public base
{
public :
 derived() : m_val(0) {}
 derived(const derived & val) : m_val(val.m_val) {}
 base & operator = (const derived& value) // POSSIBLE TO RETURN A REFERENCE TO BASE
 { m_val = value.m_val; return *this; }
 base & operator +=(const int val)
 { m_val += val; return *this; }
 /* operator + overload here */
 // base operator + (...) // IMPOSSIBLE TO RETURN A VALUE
protected :
int m_val;
};

I really really need to have a operator + defined in the base class - which in the mean time needs to remain abstract. I find it hard to achieve as a typical operator + returns a value, while an abstract class can't be instantiated - so I found myself in a bit of a catch #22 situation. Is there a valid solution to this ?

Regards

+10  A: 

Operator+() should almost always be implemented as a free function, so there is no need to declare it virtual (which youu are not doing, but must to avoid a syntax error) in base.

Basically, your design is wrong - if you need some sort of operator+()-like behaviour which a sensible implementation of operator+() cannot really provide, use a named member function instead of operator+().

anon
Summary: dynamic polymorphism and passing or returning by value do not mix.
Steve Jessop
Yes, you should have posted that as an answer - I'd vote for it!
anon
I started to, but then I noticed that everything I was planning to say was just an explanation of why (in the absence of a garbage collector) `operator+` can't really provide `operator+`-like behaviour when your clients are using polymorphic pointers. Basically the same as any textbook or tutorial would say to explain why `operator+` must return by value, not reference.
Steve Jessop
A: 

If you were allowed to do this, you could write code that doesn't make sense. If you return an abstract class by value, you've just instantiated it, and now you have an object with undefined methods running around. Also, functions have to have a consistent return type, so even your derived classes will be making instances of the abstract base, which is not what you want. There is one exception to consistent return types, covariant return types, but those only apply for pointers.

Explain what you're trying to do, and a better way can probably be found.

Joseph Garvin
A: 

Hey all, this is the OP on an alternative account.

THE PURPOSE To write an universal iterator class family that will be capable of iterating over std::vectors and T[] arrays, where T = byte = unsigned char.

THE IDEA BEHIND THE ABSTRACT CLASS

The idea was to use the base abstract class for function interfaces so that all functions would have a common type . The following abstract class enforces all children classes to employ the following operators :

** I haven't got a compiler with me atm so please forgive any errors my behalf **

class iterator
{
  public :
    virtual iterator & operator -- ()    = 0;
    virtual iterator & operator -- (int) = 0;
    virtual iterator & operator ++ ()    = 0;
    virtual iterator & operator ++ (int) = 0;
    virtual iterator & operator += (int n) = 0;
    virtual iterator & operator -= (int n) = 0;
    virtual bool operator == (const iterator & o) = 0;
    virtual bool operator != (const iterator & o) = 0;
};

So an example derived class would look like this :

/*! \brief ptr iterator can be used to iterate over classic c-style arrays */
class ptr_iterator : public iterator
{
  public :
    ptr_iterator() : m_p(NULL) {}
    ptr_iterator(byte * b) : m_p(b) {}

    iterator & operator = (const ptr_iterator &o)
    { m_p = o.m_p; return *this; }
    iterator & operator = (byte * b)
    { m_p = b; return *this; }
    virtual iterator & operator -- ()
    { --m_p; return *this; }
    virtual iterator & operator -- (int)
    { m_p--; return *this; }
    virtual iterator & operator ++ ()
    { ++m_p; return *this; }
    virtual iterator & operator ++ ()
    { m_p++; return *this; }
    virtual iterator & operator += (int n)
    { m_p += n; return *this; }
    virtual iterator & operator -= (int n)
    { m_p -= n; return *this; }
    virtual bool operator == (const iterator & o)
    { return ((ptr_iterator*)&o)->m_p == m_p; }
    virtual bool operator != (const iterator & o)
    { return ((ptr_iterator*)&o)->m_p != m_p; }

  private :
    byte * m_p;
};

Apart the forementioned iterator we can have an iterator that will iterate over an std::vector

template class std_iterator : public iterator { typedef typename C::iterator c_iterator; public : iterator & operator = (const c_iterator & i) { m_it = i; return *this; } iterator & operator = (const std_iterator & o) { m_it = o.m_it; return *this; }

virtual iterator & operator ++ ()
{ ++m_it; return *this; }
virtual iterator & operator ++ (int)
{ m_it++; return *this; }
virtual iterator & operator -- ()
{ --m_it; return *this; }
virtual iterator & operator -- (int)
{ m_it--; return *this; }
virtual iterator & operator += (int n)
{ m_it += n; return *this; }
virtual iterator & operator -= (int n)
{ m_it -= n; return *this; }

virtual bool operator == (const iterator &o)
{ return ((std_iterator*)&o)->m_it == m_it; }
virtual bool operator != (const iterator &o)
{ return ((std_iterator*)&o)->m_it != m_it; }
bool operator == (const c_iterator &i)
{ return m_it == i; }
bool operator != (const c_iterator &i)
{ return m_it != i; }

private :

c_iterator m_it;

};

Ok, so basically the forementioned iterators are capable of iterating over an array or a vector.

if we were to use a function that has the following interface :

int DoStuff(iterator &it);

All kinds of iterators would be capable of calling it - and that is the general idea. Let's say that I'm dealing with data coming in a variety of forms - ranging from arrays to vectors :)

Thing is, I need the following functionality :

std_iterator> it = my_vector.begin(); // THIS ONE WORKS std_iterator> other = it; // THIS ONE WORKS TOO

If I were inside the forementioned function

int DoStuff(iterator &it)
{
  iterator a = it + 2;   // THIS IS ILLEGAL DUE TO ABSTRACT-NESS OF THE BASE CLASS
                         // YET I STILL NEED THIS TO WORK. HOW DO I ACHIEVE THAT ?
                         // DO I SACRIFICE THE ABSTRACT-NESS OF THE BASE CLASS ?
                         // AT THIS POINT I WAS SERIOUSLY CONSIDERING AN OVERLOAD OF THE
                         // + OPERATOR BUT HAVE NO IDEA HOW TO DO IT WITH AN ABSTRACT 
                         // CLASS 
  // (...)
  return 0;
}
Maciek
it would appear that one of the solutions for the un-overloadable +/- operator would be to introduce the following :std::auto_ptr<iterator> advance(int n) = 0; // In the base classand overload it accordingly in the children classes
Maciek
A: 

We have two conflicting problems:

  1. You want to use the abstract base type as a pass-by-value type.
  2. You actually want abstract virtual behavior, with pure virtual functions.

One solution: the "pimpl" or "letter-envelope" idiom. The basic idea: create a class that wraps a pointer to an implementation class:

class impl {
public:
  virtual impl *do_add(const impl *other) const = 0;
};

class handle {
  impl *_impl;
  handle(impl *i): _impl(i) {}
public:
  handle(): _impl(NULL) {}

  handle operator+(const handle &other) const {
    impl *new_impl = _impl->do_add(other._impl);
    return handle(new_impl);
  }
};

Each virtual method in class impl has a corresponding non-virtual method in the handle. You can pass handles around by value - they're all the same size and construction as their "customization" is done via the impls they point to. And all of the underlying virtual function calls are done via pointer, so you are never creating an instance of a base impl.

You then write subclasses of impl for each specialization that you want.

One issue: you will have to be careful about memory management. You need to write a copy constructor and assignment operator for the handle. What they do with the impl is up to you. The simplest thing is to make a shallow copy of the pointer, but this is likely not sufficient, as you don't know who "owns" the pointer, i.e. when it can be freed. So, you may either have to always deep-copy the impl (i.e. _impl = other._impl->clone() where clone() is a virtual function that makes a deep copy of an impl) or use a smart pointer implementation. If you try the latter, then be sure to be const-correct and make deep copies for mutating operations.

d3jones
+1  A: 

I'm replying to your question here. (When you can, delete that "answer" and edit your OP)

You should take a look at std::advance, defined in the header <iterator>.

int DoStuff(iterator &it)
{
    iterator a = it;
    std::advance(a, 2);

    // (...)
    return 0;
}

If I'm misunderstanding your question, forgive me :P

GMan