Почему const_cast должен указывать, что вы используете? - PullRequest
4 голосов
/ 08 декабря 2010

Согласно стандарту (§5.2.11) const_cast отбрасывает квалификаторы cv (const или volatile).

Вот простой пример.Сначала вы объявляете две функции с указателем и ссылкой:

class Bar { ... };
void foo_ptr(Bar*);
void foo_ref(Bar&);

, затем создаете ссылку на const:

Bar b;
const Bar& cb = b;

, а затем вы можете вызвать любую функцию с помощьюподходящий const_cast:

foo_ptr(const_cast<Bar*>(&cb));
foo_ref(const_cast<Bar&>(cb));

Вот мой вопрос: поскольку const_cast не может делать то, для чего были разработаны другие приведения, разве не очевидно, к чему вы кидаете?Другими словами, почему язык не позволяет мне просто сказать:

foo_ptr(const_cast<>(&cb));
foo_ref(const_cast<>(cb));

Я могу думать только о следующих двух причинах:

a) Компилятор должен остановить меня, когда япопробуйте сделать что-нибудь сумасшедшее, например:

foo_ptr(const_cast<int*>(&cb));
foo_ref(const_cast<int&>(cb));

, и если вы заставите меня явно указать тип, который я на него применяю, это может удержать меня от неправильного поведения.Я нахожу это (гипотетическое) объяснение слабым, поскольку было бы странно, если бы язык позволял мне записывать что-то неправильное только для того, чтобы компилятор исправил меня.

b) Существует возможная неоднозначность, если переменнаяи постоянный и изменчивый.В этом случае компилятор не сможет сказать, пытаюсь ли я отбросить одно или другое (или оба).

Это причина или другая причина?

Ответы [ 3 ]

5 голосов
/ 08 декабря 2010

const_cast может использоваться для добавления или удаления const и volatile квалификаторов. Таким образом, если бы такой синтаксис был разрешен, все перечисленное ниже было бы допустимым целевым типом const_cast<>(&cb):

Bar*                 (1)
const Bar*           (2)
volatile Bar*        (3)
const volatile Bar*  (4)

Вы намереваетесь (1) . (2) обычно глупо, но вполне возможно, что это может произойти где-то, возможно, в некотором коде шаблона. (3) и (4) действительно являются проблемами: вы можете удалить квалификацию const и добавить квалификацию volatile для всех одним броском.

Вы можете заменить существующий const_cast парой приведений, const_cast и volatile_cast, и запретить случай (2) ; тогда вы можете использовать любой из них без целевого типа. Однако тогда сложнее узнать тип приведенного выражения. Чтобы узнать, добавляет ли выражение приведения или удаляет квалификацию, вы должны знать тип исходного выражения.

Нет причин, по которым вы не можете использовать шаблон функции, чтобы получить то, что вы хотите:

template <typename T>
T* remove_const(const T* p) {
    return const_cast<T*>(p);
}

Вы можете легко написать аналогичные функции для remove_volatile и для add_const и add_volatile, которые оба являются неявными.

1 голос
/ 08 декабря 2010

Я думаю, что Джеймс МакНеллис уже затронул важные причины.const_cast может как добавить , так и удалить постоянство / изменчивость.Не всегда понятно, что вы хотите.

Если я вызываю const_cast для аргумента типа const foo, значит ли это, что я хочу сделать его не-const или добавить к нему volatile?Или это может быть просто сохранить const квалификатор?Если я вызову его для объекта типа (non-const) foo, следует ли добавить const?Или следует предположить, что я хотел удалить константность и вернуть объект типа (неконстантный) foo?

Другой причиной может быть просто последовательность.

Мыиметь static_cast<T>(x), dynamic_cast<T>(x), reinterpret_cast<T>(x), поэтому было бы странно иметь const_cast(x) без шаблонного синтаксиса.

0 голосов
/ 07 августа 2012

Основная причина в том, что с помощью const_cast вы можете разыграть с

Foo * const * volatile * * 

до

Foo * * * volatile * const

Действительно ли очевидно, что это за тип назначения?

...