Почему const_cast удаляет константу для указателя, но не для указателя на const?

Я понимаю, что const_cast работает с указателями и ссылками.

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

Следующий код работает так, как ожидалось.

  1. const_cast с многоуровневыми указателями

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int * ip = new int(123);
        const int * ptr = ip;
        *const_cast<int*>(ptr) = 321;
        cout << "*ip: " << *ip << endl;  // value of *ip is changed to 321
    }
    

    Но когда я пытаюсь указать указатель на const int или ссылку на const int, значение, похоже, не изменится.

  2. const_cast со ссылкой на const int

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int & ri = i;
        const_cast<int&>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    
  3. const_cast с указателем на const int

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int * ri = &i;
        *const_cast<int*>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    

(1) работает так, как ожидалось, но я не могу понять, почему (2) и (3) не работают так, как я думаю, хотя вход в const_cast является указателем/ссылкой.

Пожалуйста, помогите мне понять философию этого. Благодарю.

Ответ 1

Существует два вида созвездий.

Константа объекта является неотъемлемым свойством объекта. Его нельзя изменить.

Подумайте о странице в печатной книге. Его можно рассматривать как строку символов, и его нельзя изменить. В нем говорится, что он говорит и что это. Так что это const string.

Теперь подумай о доске. На нем может быть написано что-то. Вы можете стереть это и написать что-то еще. Итак, доска - это неконстантная string.

Другой тип константы - указатель и эталонная константа. Эта константа не является неотъемлемым свойством объекта, на который указывает объект, но является разрешением. В нем говорится, что вы не можете изменять объект через этот указатель. В нем ничего не говорится о том, можно ли изменить сам объект.

Поэтому, если у вас есть указатель const, вы не обязательно знаете, на что он действительно указывает. Возможно, это книга. Может, это доска. Указатель не говорит.

Теперь, если вы действительно знаете, что это действительно доска, вы можете быть противным и требовать разрешения идти дальше и изменять то, что написано на нем. Это то, что делает const_cast. Это дает вам разрешение на выполнение чего-то.

Что произойдет, если вы требуете разрешения на изменение строки и, оказывается, это печатная страница? Вы получаете свое разрешение, вы идете вперед и вытираете его... и... То, что именно происходит, не определено. Возможно, вообще ничего. Возможно, печать размазана, и вы не можете ни распознать оригинальную строку, ни написать что-нибудь сверху. Возможно, ваш мир взрывается на мелкие кусочки. Вы можете попробовать и посмотреть, но нет гарантии, что то же самое произойдет завтра.

Ответ 2

(2) и (3) имеет тот же принцип, поэтому я буду говорить только о (2).

Линия

const_cast<int&>(ri) = 321;

имеет неопределенное поведение.

Вы не можете изменять объект const соответствии со стандартом, даже с const_cast. Если вы удалите const из указателя/ссылки и измените объект с указателем/ссылкой, объект с указанием/ссылкой не должен быть объявлен как const в первую очередь.

const_cast следует использовать только, когда у вас есть указатель const на что-то по какой-то причине, и вы знаете, что что-то не объявлено как const.

Ответ 3

Изменение константы через const_cast - неопределенное поведение.

Компилятор видит, что вы пытаетесь напечатать постоянную переменную, знает, что она никогда не сможет изменить такие компиляции:

cout << "i: " << i << endl;

чтобы:

cout << "i: " << 123 << endl;

см. https://godbolt.org/z/bYb0mx. Оптимизация позволяет оптимизировать код только для печати 123: https://godbolt.org/z/4Ttlmj.

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