Ответ: это зависит от того, по какому стандарту C ++ вы компилируете. Весь код идеально сформирован по всем стандартам ‡ за исключением этой строки:
char * s = "My String";
Теперь строковый литерал имеет тип, const char[10]
и мы пытаемся инициализировать на него неконстантный указатель. Для всех других типов, кроме char
семейства строковых литералов, такая инициализация всегда была недопустимой. Например:
const int arr[] = {1};
int *p = arr; // nope!
Однако до C ++ 11 для строковых литералов было исключение в §4.2 / 2:
Строковый литерал (2.13.4), который не является широким строковым литералом, может быть преобразован в rvalue типа « указатель на char »; [...]. В любом случае результатом будет указатель на первый элемент массива. Это преобразование учитывается только при наличии явного соответствующего целевого типа указателя, а не тогда, когда есть общая потребность в преобразовании из lvalue в rvalue. [Примечание: это преобразование устарело . См. Приложение D. ]
Итак, в C ++ 03 код прекрасен (хотя и не рекомендуется) и имеет четкое, предсказуемое поведение.
В C ++ 11 этого блока не существует - такого исключения для преобразованных строковых литералов нет char*
, поэтому код так же плохо сформирован, как и int*
приведенный мною пример. Компилятор обязан выдавать диагностику, и в идеале в таких случаях, как этот, которые являются явным нарушением системы типов C ++, мы ожидаем, что хороший компилятор не только будет соответствовать в этом отношении (например, выдав предупреждение), но и не сможет прямо.
В идеале код не должен компилироваться, но он выполняется как для gcc, так и для clang (я предполагаю, потому что, вероятно, существует много кода, который будет сломан с небольшим выигрышем, несмотря на то, что эта системная дыра не рекомендуется более десяти лет). Код плохо сформирован, и поэтому нет смысла рассуждать о том, каким может быть поведение кода. Но, учитывая этот конкретный случай и его ранее разрешенную историю, я не считаю необоснованным интерпретировать полученный код, как если бы он был неявным const_cast
, например:
const int arr[] = {1};
int *p = const_cast<int*>(arr); // OK, technically
С остальной частью программы все в порядке, поскольку вы больше никогда не прикасаетесь к ней s
. Чтение созданного const
объекта через не const
указатель - это нормально. Запись созданного const
объекта через такой указатель является неопределенным поведением:
std::cout << *p; // fine, prints 1
*p = 5; // will compile, but undefined behavior, which
// certainly qualifies as "unpredictable"
Поскольку s
нигде в вашем коде нет никаких изменений , программа работает в C ++ 03, не должна компилироваться в C ++ 11, но все равно делает - и, учитывая, что компиляторы это позволяют, в ней все еще нет неопределенного поведения † . С учетом того, что компиляторы все еще [неверно] интерпретируют правила C ++ 03, я не вижу ничего, что привело бы к «непредсказуемому» поведению. Напишите s
хотя бы, и все ставки отменены. Как в C ++ 03, так и в C ++ 11.
† Хотя, опять же, по определению плохо сформированный код не дает ожидаемого разумного поведения
‡ За исключением того, что см . Ответ Мэтта Макнабба.