Марк B переходит к фундаментальным соображениям, но обратите внимание, что вы можете сделать нечто подобное в чистом C ++. Рассмотрим:
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(); }
};
Вместо const Image *
используйте ConstImage *
, и все. Вы также можете просто определить псевдо-конструктор статической функции:
const Image *Image::newConstImage(const Data *d) {
return new Image(const_cast<Data*>(d));
}
Это, конечно, полагается на программиста, чтобы гарантировать, что нет никаких функций const
, которые могли бы каким-либо образом изменять состояние, на которое указывает Data
.
Вы также можете комбинировать эти приемы:
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));
}
};
Это получает лучшее из обоих миров; поскольку data()
неконстантный член, у вас есть статическая проверка на наличие мутации указанного значения. Однако у вас также есть конструктор const, и вы можете напрямую приводить значения между Image *
и const Image *
(то есть вы можете удалить константу, если знаете, что она безопасна).
Вы также можете абстрагироваться от разделения указателей далее:
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));
}
};
Теперь, если this
является константой, data
становится константой, распространяя это также на *data
. Достаточно хорошо для тебя? :)
Полагаю, окончательный ответ, вероятно, таков: чтобы конструктор const был полезным и безопасным, нам нужно что-то вроде ConstPropPointer
, которое вы видите там встроенным в язык. Конструкторам Const будет разрешено присваивать от const T *
до constprop T *
. Это сложнее, чем кажется - например, как это взаимодействует с шаблонными классами, такими как vector
?
Итак, это несколько сложное изменение, но проблема, похоже, не так уж и актуальна. Что еще более важно, здесь есть простой обходной путь (ConstPropPointer
можно добавить в библиотеку, а статический псевдо-конструктор достаточно просто добавить). Таким образом, комитет C ++, вероятно, передал его для более важных вещей, если он вообще был предложен.