Зверніть увагу, якщо це підходить вам, але у функціональних мовах, таких як Standard ML, все заміна за замовчуванням. Мутація підтримується через загальний refтип еренції. Отже intзмінна є незмінною, а ref intзмінна є контейнером, що змінюється, для ints. В основному, змінні - це реальні змінні в математичному сенсі (невідоме, але фіксоване значення), а refs - "змінні" в сенсі імперативного програмування - комірка пам'яті, в яку можна записувати та читати. (Я люблю їх називати присвоюваними .)
Я думаю, що проблема з constдвома проблемами . По-перше, у C ++ бракує збору сміття, що необхідно для створення нетривіальних стійких структур даних .const має бути глибоким, щоб мати сенс, але мати цілком незмінні значення в C ++ не доцільно.
По-друге, в C ++ вам потрібно ввімкнути, constа не відмовитися від нього. Але коли ви constщось забудете і пізніше це виправите, ви опинитесь у ситуації "отруєння Const", згаданої у відповіді @ RobY, де constзміна відбуватиметься протягом усього коду. Якби constза замовчуванням ви не застосували б constзаднім числом. Крім того, необхідність додавати constвсюди додає багато шуму коду.
Я підозрюю, що основні мови, які слідують за ними (наприклад, Java), сильно формували успіхи та спосіб мислення C та C ++. Справа в тому, що навіть при збиранні сміття більшість API колекціонування мов передбачають змінні структури даних. Той факт, що все є незмінним і незмінним, сприймається як кутовий випадок, багато в чому говорить про імперативний спосіб мислення, що стоїть за популярними мовами.
EDIT : Після роздуму над коментарем Greenoldman я зрозумів, що constсправа не стосується незмінності даних; constкодує у тип методу, чи має він побічні ефекти на екземпляр.
Мутацію можна використовувати для досягнення референтно прозорої поведінки. Припустимо, у вас є функція, яка при виклику послідовно повертає різні значення - наприклад, функцію, з якої читається один символ stdin. Ми могли б використовувати кеш / запам'ятати результати цієї функції для створення референтно прозорого потоку значень. Потік буде зв'язаним списком, вузли якого викличуть функцію при першому спробі отримати їх значення, але потім кешують результат. Отже, якщо stdinмістить Hello, world!, перший раз, коли ви намагаєтеся отримати значення першого вузла, він прочитає його charта повернеться H. Після цього він буде продовжувати повертатися, коли ви намагаєтеся отримати його значення, цього разу повертаючи та кешуючи цей результат.H без додаткових дзвінків для читання char. Аналогічно, другий вузол буде читати a charзstdine
Цікавим є те, що ви перетворили процес, який за своєю суттю є державним, в об'єкт, який, здавалося б, є без громадянства. Однак, щоб досягти цього, потрібно було мутувати внутрішній стан об'єкта (кешуючи результати) - мутація мала доброякісний ефект . Неможливо зробити наше, CharStream constнавіть якщо потік поводиться як незмінна цінність. Тепер уявіть, що тут є Streamінтерфейс з constметодами, і всі ваші функції очікують const Streams. Ви CharStreamне можете реалізувати інтерфейс!
( EDIT 2: Мабуть, є ключове слово C ++, mutableяке б дозволило нам обманювати і створюватиCharStream const . Однак ця лазівка руйнує constгарантії - тепер ви справді не можете бути впевнені, що щось не вимкне його constметодами. Я припускаю, що це не так погано, оскільки потрібно явно вимагати лазівку, але ви все ще повністю покладаєтесь на систему честі.)
По-друге, припустимо, у вас є функції високого порядку - тобто ви можете передавати функції як аргументи іншим функціям. constness є частиною підпису функції, тому ви не зможете передавати нефункції constяк аргументи функціям, які очікують constфункцій. Сліпо примусове виконання constтут призведе до втрати загальності.
Нарешті, маніпулювання constоб'єктом не гарантує, що воно не мутує якесь зовнішнє (статичне або глобальне) стан за вашою спиною, тому constгарантії не такі сильні, як вони з'являються спочатку.
Мені не зрозуміло, що кодування наявності чи відсутності побічних ефектів у типовій системі є загальноприйнятною справою.