Читать «Стандарты программирования на С++. 101 правило и рекомендация» онлайн - страница 145

Андрей Александреску

Даже если ваша программа не потерпит крах, отмена const представляет собой отмену обещанного и не делает того, чего от нее зачастую ожидают. Например, в приведенном фрагменте не происходит выделения массива переменной длины:

void Foolish(unsigned int n) {

 const unsigned int size = 1;

 const_cast<unsigned int&>(size) = n; // Не делайте так!

 char buffer[size];                   // Размер массива

 // ...                               // все равно равен 1

}

В С++ имеется одно неявное преобразование const_cast из строкового литерала в char*:

char* weird = "Trick or treat?";

Компилятор молча выполняет преобразование const_cast из const char[16] в char*. Это преобразование позволено для совместимости с API в стиле С, хотя и представляет собой дыру в системе типов С++. Строковые литералы могут размещаться в памяти только для чтения, и попытка изменения такой строки может вызвать нарушение защиты памяти.

Исключения

Преобразование, отменяющее const, может оказаться необходимым для вызова функции API, некорректно указывающей константность (см. рекомендацию 15). Оно также полезно, когда функция, которая должна получать и возвращать ссылку одного и того же типа, имеет как константную, так и неконстантную перегрузки, причем одна из них вызывает другую:

const Object& f(const Object&);

Object& f(Object& obj {

 const Object& ref = obj;

 return const_cast<Object&>(f(ref)); // преобразование

}                                    // возвращаемого типа

Ссылки

[Dewhurst03] §32, §40 • [Sutter00] §44

95. Не используйте преобразование типов в стиле С

Резюме

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

Обсуждение

Одна из проблем, связанных с преобразованием типов в стиле С, заключается в том, что оно использует один и тот же синтаксис для выполнения несколько разных вещей, в зависимости от таких мелочей, как, например, какие именно заголовочные файлы включены при помощи директивы #include. Преобразования типов в стиле С++, сохраняя определенную опасность, присущую преобразованиям вообще, имеют четко документированное предназначение, их легко найти, дольше писать (что дает время дважды подумать при их использовании), и не позволяют незаметно выполнить опасное преобразование reinterpret_cast (см. рекомендацию 92).

Рассмотрим следующий код, в котором Derived — производный от базового класса Base: