Відповідь: це залежить від того, на який C ++ стандарт ви компілюєте. Весь код ідеально сформований у всіх стандартах ‡ за винятком цього рядка:
char * s = "My String";
Тепер рядковий літерал має тип, const char[10]і ми намагаємося ініціалізувати на нього неконст-покажчик. Для всіх інших типів, крім charсімейства рядкових літералів, така ініціалізація завжди була незаконною. Наприклад:
const int arr[] = {1};
int *p = arr; // nope!
Однак у програмі pre-C ++ 11 для літеральних рядків був виняток у §4.2 / 2:
Строковий літерал (2.13.4), який не є широким літеральним рядком, може бути перетворений у рецензію типу " вказівник на char "; [...]. В будь-якому випадку результат є вказівником на перший елемент масиву. Це перетворення враховується лише тоді, коли є явний відповідний цільовий тип вказівника, а не тоді, коли є загальна потреба перетворення з lvalue в rvalue. [Примітка. Ця конверсія застаріла . Див. Додаток D. ]
Тож у C ++ 03 код ідеально чудовий (хоча і застарілий) та має чітку передбачувану поведінку.
У C ++ 11 цього блоку не існує - не існує такого винятку для рядкових літералів, перетворених у char*, і тому код настільки ж неправильно сформований, як int*я щойно наводив приклад. Компілятор зобов’язаний випустити діагностику, і в ідеалі у таких випадках, як це явні порушення системи типу C ++, ми очікуємо, що хороший компілятор не просто відповідатиме в цьому плані (наприклад, видавши попередження), але і не зможе відверто.
Код в ідеалі не повинен компілюватись, але він діє як на gcc, так і на кланг (я припускаю, оскільки там, мабуть, багато коду, який би був зламаний з невеликим коефіцієнтом посилення, незважаючи на те, що цей тип системного отвору припиняється протягом більше десяти років). Код неправильно сформований, і тому не має сенсу міркувати про те, якою може бути поведінка коду. Але, враховуючи цей конкретний випадок і історію його раніше дозволеного, я не вважаю, що це нерозумно розтягнути інтерпретацію отриманого коду як би неявну 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.
† Хоча, знову ж таки, за визначенням неправильно сформований код не дає очікувань розумної поведінки
‡ Як ні, дивіться відповідь Метта Макнабба