Читать «Стандарты программирования на С++. 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)); // преобразование
} // возвращаемого типа
Ссылки
95. Не используйте преобразование типов в стиле С
Резюме
Возраст не всегда означает мудрость. Старое преобразование типов в стиле С имеет различную (и часто опасную) семантику в зависимости от контекста, спрятанную за единым синтаксисом. Замена преобразования типов в стиле С преобразованиями С++ поможет защититься от неожиданных ошибок.
Обсуждение
Одна из проблем, связанных с преобразованием типов в стиле С, заключается в том, что оно использует один и тот же синтаксис для выполнения несколько разных вещей, в зависимости от таких мелочей, как, например, какие именно заголовочные файлы включены при помощи директивы #include
. Преобразования типов в стиле С++, сохраняя определенную опасность, присущую преобразованиям вообще, имеют четко документированное предназначение, их легко найти, дольше писать (что дает время дважды подумать при их использовании), и не позволяют незаметно выполнить опасное преобразование reinterpret_cast
(см. рекомендацию 92).
Рассмотрим следующий код, в котором Derived
— производный от базового класса Base
: