views:

150

answers:

3

I have two classes, point and pixel:

class point {
    public:
        point(int x, int y) : x(x), y(y) { };
    private:
        int x, y;
}

template <class T>
class pixel : public point {
    public:
        pixel(int x, int y, T val) : point(x, y), val(val) { };
    private:
        T val;
}

Now here's my problem. I want to make a container class (let's call it coll) that has a private vector of points or pixels. If an instance of coll contains pixels, I want it to have a method toArray(), which converts its vector of pixels to an array of T representing the contents of the vector.

I was going to do this with inheritance: ie, I could make a base class coll that contains a vector of points and a derived class that contains the extra method, but then I seem to run into problems since pixel is a class template.

Does anyone have suggestions? Could I do this somehow by making coll a class template?

+2  A: 

Question: Do you mean for the private vector to contain both Points and Pixels at the same time, or just one or the other?

Question: If just one or the other, are you meaning to mix Pixels with different template parameters in the same private vector?

Assuming that it is just Point or Pixel in the private vector, and that the Pixels in the private vector all have the same template parameter, you could do something like this:

template < class T > class CollectionBase
{
   //common interface here
   protected:
   std::vector<T> coll_vec;
};

class PointCollection : public CollectionBase<Point>
{
   public:
   ...
};

template< class T> PixelCollection : public CollectionBase<Pixel<T> >
{
   public:
    Pixel<T>* toArray();

    ...

};
diverscuba23
Answer: just one or the other.Answer: just pixels with the same template parameter.This solution seems pretty good, but my collections will have lots of the same functionality: won't this require lots of duplication of the same code since CollectionBase won't have access to the containers?
amc
@amc updated answer to prevent code duplication. It kinda makes the PointCollection a bit redundant, but this will prevent most casts from PixelCollection to PointCollection and vice versa. If you intend to allow them to be cast between each other then you could just collapse CollectionBase and PointCollection together into a single class.
diverscuba23
AWESOME, this is exactly what I wanted. I guess I didn't realize that you could subclass a template class. Thanks!
amc
A: 

If you want to check whether a point object is also a type of pixel<T>, then you can simply see if dynamic_cast returns NULL. In order to do this, point will need to be polymorphic, so add a virtual destructor to it.

Here's an example:

point x(0, 0);
pixel<int> y(0, 0, 0);
point *pX = &x;
point *pY = &y;
if(dynamic_cast<pixel<int> *> (pX) != NULL) {
    std::cout << "x is a pixel<int>.";
}
if(dynamic_cast<pixel<int> *> (pY) != NULL) {
    std::cout << "y is a pixel<int>.";
}

The output is as follows:

y is a pixel<int>.

You could use this code in your coll class to check whether each element of a vector<point *> is a point or a pixel. But in order to do so, you would need to know which specialisation of pixel is being stored (i.e. is it a pixel<int> or a pixel<float>?)

It may be simpler to make coll a class template instead.

robinjam
A: 

If collection treats points and pixels mostly the same and only contains one or the other, it makes sense to make it a template class.
Regarding to_array however, it might be simpler to make it a free function instead:

template<class T> struct collection {
    std::vector<point<T> > data_;
    // ...
};

template<class T>
void to_array(const collection<point<T> >& in, point<T>* out) {
    // ...
}

Note that you would have to provide a public interface for read-access to the data however or at least selectively grant to_array() access.

Georg Fritzsche