Background
I'm writing an image handling class. For this question two requirements of the class are of interest:
- Must have "deep" const correctness.
- Must allow sub-image aliases, a.k.a. sections or slicing without performing deep copies of image data.
By deep constness I mean the following. Consider this example class:
class Image{
public:
...
Image(const Image&);
Image alias(int x0, int y0, int x1, int y1) const;
private:
Buffer image_data;
};
And assume you have a function that is passed a const reference to a image, like so:
void foo(const Image& im){
Image alias = im.alias(0,0, 10, 10);
// Hold on, the mutable alias can modify the data in the const argument!
}
The above alias
function call defeats the constness of the argument because the contents of the argument image can be modified through the non-const alias. I can't return a const Image
from alias
because well that makes no sense really and the copy constructor of Image
will happily construct a mutable instance anyway.
My solution (and I've seen it used often so I don't claim originality here) is to introduce two classes (interfaces really, but for the simplicity they are classes in this example and return by value is okay, in reality alias(...)
returns a shared_ptr
to interface):
class ConstImage{
public:
ConstImage alias(...) const;
};
class Image{
public:
Image alias(...);
};
Now the following code:
void foo(const Image& im){
// compile error, cannot convert ConstImage to Image, perfect!
Image alias = im.alias(...);
}
And of course you would change the signature to void foo(const ConstImage& im)
so that you can accept invocations like foo(const_im.alias(...))
or other ConstImage
objects.
Now of course we want foo(const ConstImage& im)
to be callable (1) with a mutable Image
too and here is where the question comes in.
Question
Using inheritance: class Image : public ConstImage
will make all Image
types usable through ConstImage
references and pointers. It will also facilitate code-reuse between the const and non-const implementations which by necessity are very similar.
This solves the above mentioned problem (1). However if you look at the inheritance relation as an "is a" statement then "a mutable image IS AN immutable image" makes no sense. Which speaks against using inheritance here.
My question is, is this kind of (ab)use of inheritance widely acceptable? Is there some other better solution that I have overlooked? Note that the Image
class is more of an interface so naive value conversion from Image
to ConstImage
isn't possible. Although one could probably come up with something crazy to allow it.
alias()
return a simplerImageRef
class (somewhat inspired by string_ref from Boost), that only hasconst
methods and holds a non-owning reference/pointer to the underlaying data.alias() const
must be a first classImage
. There will also be a mutating version ofalias()
.Circle
inherit fromEllipse
may be a bad idea. The C++ FAQ from isocpp.org explains it really well, see isocpp.org/wiki/faq/proper-inheritance#circle-ellipse and the subsequent FAQs.