Відповідь: це залежить від того, на який 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.
† Хоча, знову ж таки, за визначенням неправильно сформований код не дає очікувань розумної поведінки
‡ Як ні, дивіться відповідь Метта Макнабба