Является ли законным изменять объект, созданный с помощью нового с помощью указателя const?

Итак этот ответ заставило меня задуматься о сценарии, в котором вы присваиваете результат new указателю на const. AFAIK, нет причин, по которым вы не можете юридически const_cast установить и фактически изменить объект в этой ситуации:

struct X{int x;};

//....
const X* x = new X;
const_cast<X*>(x)->x = 0; // okay

Но потом я подумал - что, если вы действительно хотите new создать объект const. Поэтому я попробовал

struct X{};

//....
const X* x = new const X;

и он скомпилирован!!!

Является ли это расширением GCC или стандартным поведением? У меня никогда это не наблюдается на практике. Если это стандартно, я начну использовать его, когда это возможно.

Ответы

Ответ 1

const является частью типа. Неважно, выделяете ли вы свой объект с динамической, статической или автоматической продолжительностью хранения. Он по-прежнему const. Отбрасывание, что const ness и мутация объекта все равно будет выполняться undefined.

const ness - абстракция, которую система типов дает нам для обеспечения безопасности вокруг неперемещаемых объектов; он делает это в значительной степени, чтобы помочь нам во взаимодействии с постоянной памятью, но это не значит, что его семантика ограничена такой памятью. Действительно, С++ даже не знает, что такое и не является доступной для чтения памятью.

Помимо того, что это выводимое из всех обычных правил, без исключения [lol] для объектов с динамическим распределением, стандарты упоминают это явно (хотя и в примечании):

[C++03: 5.3.4/1]: Новое выражение пытается создать объект типа-id (8.1) или new-type-id, к которому он применяется. Тип этого объекта - это назначенный тип. Этот тип должен быть полным типом объекта, но не абстрактным типом класса или его массивом (1.8, 3.9, 10.4). [Примечание: поскольку ссылки не являются объектами, ссылки не могут быть созданы новыми выражениями. ] [Примечание: идентификатор типа может быть классом с квалификацией cv, и в этом случае объект, созданный новым выражением, имеет тип с квалификацией cv.] [..]

[C++11: 5.3.4/1]: Новое выражение пытается создать объект идентификатора типа (8.1) или нового типа, к которому он применяется. Тип этого объекта - это назначенный тип. Этот тип должен быть полным типом объекта, но не абстрактным типом класса или его массивом (1.8, 3.9, 10.4). Определяется реализация, поддерживаются ли надстрочные типы (3.11). [Примечание: поскольку ссылки не являются объектами, ссылки не могут быть созданы новыми выражениями. -end note] [Примечание: идентификатор типа может быть классом с квалификацией cv, и в этом случае объект, созданный новым выражением, имеет тип с квалификацией cv. -end note] [..]

Здесь также приведен пример использования в [C++11: 7.1.6.1/4].

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

Ответ 2

new явно не создает объект const (надеюсь).

Если вы спросите new создать объект const, вы получите объект const.

нет причин, по которым вы не можете юридически const_cast устоять и фактически изменить объект.

Есть. Причина в том, что спецификация языка вызывает это явно как поведение undefined. Таким образом, вы можете, но это почти ничего не значит.

Я не знаю, чего вы ожидали от этого, но если вы считали, что проблема заключается в распределении памяти в readonly или нет, это далеко не так. Это не имеет значения. Компилятор может предположить, что такой объект не может быть изменен и оптимизирован соответствующим образом, и вы получите неожиданные результаты.

Ответ 3

Мой способ взглянуть на это:

  • X и const X, а указатели на них - разные типы.
  • существует неявное преобразование от X* до const X*, но не наоборот
  • поэтому следующие юридические и X в каждом случае имеет идентичный тип и поведение

    const X * x = новый X; const X * x = new const X;

Единственный оставшийся вопрос заключается в том, может ли другой распределитель быть вызван во втором случае (возможно, в памяти только для чтения). Ответ отрицательный, в стандарте такого положения нет.