Mark B goes over the fundamental considerations, but note that you can do something similar in pure C++. Consider:
struct Data { };
class ConstImage {
protected:
const Data *const_data;
public:
ConstImage (const Data *cd) : const_data(cd) { }
int getFoo() const { return const_data->getFoo(); }
};
class Image : public ConstImage {
protected:
Data *data() { return const_cast<Data *>(const_data); }
public:
Image(Data *d) : const_data(d) { }
void frob() { data()->frob(); }
};
Instead of using const Image *
, use ConstImage *
, and there you go. You could also simply define a static function pseudo-constructor:
const Image *Image::newConstImage(const Data *d) {
return new Image(const_cast<Data*>(d));
}
This, of course, relies on the programmer to ensure that there aren't any const
functions which might somehow mutate the pointed-to Data
's state.
You can also combine these techniques:
class Image {
protected:
const Data *const_data;
Data *data() { return const_cast<Data *>(const_data); }
public:
void frob() { data()->frob(); }
int getFoo() const { return const_data->getFoo(); }
Image(Data *d) : const_data(d) { }
static const Image *newConst(const Data *cd) {
return new Image(const_cast<Data *>(cd));
}
};
This gets the best of both worlds; since data()
is a non-const member, you have static checking for mutation of the pointed-to value. You also, however, have a const constructor, and can directly cast between Image *
and const Image *
(ie, you can remove the constness if you know it is safe).
You can also abstract away the separation of pointers further:
template<typename T>
class ConstPropPointer {
private:
T *ptr;
public:
ConstPropPointer(T *ptr_) : ptr(ptr_) { }
T &operator*() { return *ptr; }
const T &operator*() const { return *ptr; }
T *operator->() { return ptr; }
const T *operator->() const { return ptr; }
};
class Image {
protected:
ConstPropPointer<Data> data;
public:
void frob() { data->frob(); }
int getFoo() const { return data->getFoo(); }
Image(Data *d) : data(d) { }
static const Image *newConst(const Data *cd) {
return new Image(const_cast<Data *>(cd));
}
};
Now, if this
is const, data
becomes const, propagating that into *data
as well. Good enough for you? :)
I suppose the final answer is probably this: In order for a const constructor to be useful and safe, we'd need something like the ConstPropPointer
you see there built into the language. Const constructors would then be allowed to assign from const T *
to constprop T *
. This is more complex than it sounds - for example, how does this interact with template classes such as vector
?
So, this is a somewhat complex change, but the problem doesn't seem to come up all that much. More importantly, there's a simple workaround here (the ConstPropPointer
can be librarized, and the static pseudo-constructor is simple enough to add). So the C++ committee probably passed it over for more important things, if it was even proposed at all.