В C, C ++, D, JavaScript и Джулия языки программирования, const - это квалификатор типа : ключевое слово , примененное к типу данных, что означает, что данные доступны только для чтения. Хотя это можно использовать для объявления констант, const
в семействе языков C отличается от аналогичных конструкций в других языках тем, что является частью типа и, следовательно, имеет сложное поведение в сочетании с указатели, ссылки, составные типы данных и проверка типов.
При применении в объявление объекта , оно указывает, что объект является константой : его значение не может быть изменено, в отличие от переменной . Это основное использование - объявление констант - имеет параллели во многих других языках.
Однако, в отличие от других языков, в семействе языков C const
является частью типа, а не частью объекта. Например, в C int const x = 1;
объявляет объект x
типа int const
- const
является частью тип, как если бы он был проанализирован "(int const) x" - в то время как в Ada, X: constant INTEGER: = 1_
объявляет константу (вид объекта) X
типа INTEGER
: константа является частью объекта, но не является частью типа.
Это дает два незаметных результата. Во-первых, const
может применяться к частям более сложного типа - например, int const * const x;
объявляет постоянный указатель на постоянное целое число, а int const * x;
объявляет указатель переменной на постоянное целое число, а int * const x;
объявляет постоянный указатель на переменное целое число. Во-вторых, поскольку const
является частью типа, он должен соответствовать как часть проверки типа. Например, следующий код недействителен:
void f (int x); //... int const i; f (i);
, потому что аргумент f
должен быть переменным целым числом, но i
является постоянным целым числом. Это соответствие является формой корректности программы и известно как константная корректность . Это позволяет использовать форму программирования по контракту, где функции указывают как часть своей сигнатуры типа, изменяют ли они свои аргументы или нет, и является ли их возвращаемое значение модифицируемый или нет. Эта проверка типов в первую очередь интересна для указателей и ссылок - не для базовых типов значений, таких как целые числа, - но также для составных типов данных или шаблонных типов, таких как контейнеры. Это скрыто тем фактом, что const
часто можно опустить из-за приведения типа (неявное преобразование типа ) и C, являющегося вызываемым -value (C ++ и D являются либо вызовом по значению, либо вызовом по ссылке).
Идея постоянства не подразумевает, что переменная, хранящаяся в компьютерной памяти, недоступна для записи. Скорее, const
-ness - это конструкция времени компиляции, которая указывает, что должен делать программист, не обязательно то, что он может делать. Однако обратите внимание, что в случае предопределенных данных (таких как char const *
строковые литералы ) C const
часто не может быть записан.
Хотя константа не меняет своего значения во время работы программы, объект, объявленный const
, действительно может изменить свое значение во время работы программы. Типичный пример - регистры только для чтения во встроенных системах, такие как текущее состояние цифрового входа. Регистры данных для цифровых входов часто объявляются как const
и volatile
. Содержимое этих регистров может измениться без каких-либо действий программы (volatile
), но вы также не должны записывать в них (const
).
Кроме того, (нестатическая) функция-член может быть объявлена как const
. В этом случае this
указатель внутри такой функции имеет тип object_type const *
, а не просто тип object_type *
. Это означает, что неконстантные функции для этого объекта нельзя вызывать изнутри такой функции, а также нельзя изменять переменные-члены. В C ++ переменная-член может быть объявлена как mutable
, что указывает на то, что это ограничение к ней не применяется. В некоторых случаях это может быть полезно, например, с кэшированием, подсчетом ссылок и синхронизацией данных. В этих случаях логическое значение (состояние) объекта не изменяется, но объект не является физически постоянным, поскольку его побитовое представление может измениться.
В C, C ++ и D все типы данных, в том числе определенные пользователем, могут быть объявлены const
, а константная корректность требует, чтобы все типы данных переменные или объекты должны быть объявлены как таковые, если их не нужно изменять. Такое проактивное использование const
делает значения «более легкими для понимания, отслеживания и рассуждений» и, таким образом, повышает читабельность и понятность кода, а также упрощает работу в группах и сопровождение кода, поскольку передает информацию о значение по назначению. Это может помочь компилятору , а также разработчику при рассмотрении кода. Он также может позволить оптимизирующему компилятору генерировать более эффективный код.
Для простых типов данных без указателей применение const
квалификатор прост. По историческим причинам он может идти по обе стороны от некоторых типов (например, const char foo = 'a';
эквивалентно char const foo = 'a';
). В некоторых реализациях использование const
дважды (например, const char const
или char const const
) генерирует предупреждение, но не ошибку.
Для типов указателей и ссылок значение const
более сложное - либо сам указатель, либо значение, на которое указывает, либо и то, и другое, может быть const
. Кроме того, синтаксис может сбивать с толку. Указатель может быть объявлен как указатель const
на доступное для записи значение, или доступный для записи указатель на значение const
, или как const
указатель на const
значение. Указатель const
нельзя переназначить так, чтобы он указывал на объект, отличный от того, который ему изначально назначен, но его можно использовать для изменения значения, на которое он указывает (называемого указателем). Ссылочные переменные в C ++ - это альтернативный синтаксис для указателей const
. С другой стороны, указатель на объект const
можно переназначить так, чтобы он указывал на другую ячейку памяти (которая должна быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменить память, на которую он указывает. Указатель const
на объект const
также может быть объявлен и не может быть использован для изменения объекта-указателя или переназначен для указания на другой объект. Следующий код иллюстрирует эти тонкости:
void Foo (int * ptr, int const * ptrToConst, int * const constPtr, int const * const constPtrToConst) {* ptr = 0; // OK: изменяет данные "указателя" ptr = NULL; // ОК: изменяет указатель * ptrToConst = 0; // Ошибка! Невозможно изменить данные "указателя" ptrToConst = NULL; // ОК: изменяет указатель * constPtr = 0; // ОК: изменяет данные "указателя" constPtr = NULL; // Ошибка! Невозможно изменить указатель * constPtrToConst = 0; // Ошибка! Невозможно изменить данные "указателя" constPtrToConst = NULL; // Ошибка! Невозможно изменить указатель}
Следуя обычному соглашению C для объявлений, объявление следует за использованием, и *
в указателе записывается на указателе, указывая на разыменование . Например, в объявлении int * ptr
разыменованная форма * ptr
- это int
, а ссылочная форма ptr
- это указатель на int
. Таким образом, const
изменяет имя справа. Соглашение C ++ вместо этого заключается в том, чтобы связать *
с типом, как в int * ptr,
, и прочитать const
как изменение типа слева. int const * ptrToConst
, таким образом, можно прочитать как «* ptrToConst
is a int const
» (значение является постоянным) или «ptrToConst
- это int const *
"(указатель является указателем на постоянное целое число). Таким образом:
int * ptr; // * ptr - это значение типа int int const * ptrToConst; // * ptrToConst - это константа (int: целочисленное значение) int * const constPtr; // constPtr - константа (int *: целочисленный указатель) int const * const constPtrToConst; // constPtrToConst - это константа (указатель) // как есть * constPtrToConst (value)
Следуя соглашению C ++ об анализе типа, а не значения, практическое правило стоит читать декларацию справа налево. Таким образом, все, что находится слева от звезды, может быть идентифицировано как тип указателя, а все справа от звезды является свойствами указателя. Например, в нашем примере выше int const *
может быть прочитан как доступный для записи указатель, который относится к незаписываемому целому числу, а int * const
может быть прочитан как не- доступный для записи указатель, который ссылается на доступное для записи целое число.
Более общее правило, которое помогает вам понять сложные объявления и определения, работает следующим образом:
Вот пример:
Часть выражения | double (** const (* fun (int)) (double)) [10] | Значение. (чтение вниз) |
---|---|---|
Идентификатор | fun | fun - это... |
Считывание вправо | (int)) | функция, ожидающая int... |
Найдите совпадение ( | (* | возвращает указатель на... |
Продолжить вправо | (double)) | функция, ожидающая двойного... |
Найти совпадающие ( | (** const | , возвращающие постоянный указатель на. указатель на... |
Продолжить вправо | [10] | блоков 10... |
Читать слева | double | doubles. |
При чтении слева важно читать элементы справа налево. Так что int const *
становится указателем на const int, а не константным указателем на int.
В некоторых случаях C / C ++ позволяет размещать ключевое слово const
слева от типа. Вот несколько примеров:
const int * ptrToConst; // identity l к: int const * ptrToConst, const int * const constPtrToConst; // идентично: int const * const constPtrToConst
Хотя C / C ++ допускает такие определения (которые близко соответствуют английскому языку при чтении определений слева направо), компилятор по-прежнему считывает определения в соответствии с вышеупомянутой процедурой: from справа налево. Но если поставить const
перед тем, что должно быть константой, быстро появятся несоответствия между тем, что вы собираетесь написать, и тем, что компилятор решил, что вы написали. Рассмотрим указатели на указатели:
int ** ptr; // указатель на указатель на целые числа int const ** ptr // указатель на указатель на постоянное значение int // (не указатель на постоянный указатель на целые числа) int * const * ptr // указатель на константу указатель на значения типа int // (не постоянный указатель на указатель на целые числа) int ** const ptr // постоянный указатель на указатели на целые значения // (ptr, идентификатор, являющийся константой, не имеет смысла) int const ** const ptr // постоянный указатель на указатели на постоянные значения типа int
В качестве последнего примечания относительно определений указателей: всегда пишите символ указателя (*) как можно правее. Прикрепить символ указателя к типу сложно, так как он настоятельно предполагает тип указателя, что не так. Вот несколько примеров:
int * a; / * пишем: * / int * a; // a - указатель на int int * a, b; // ЗАБОТА / * запись: * / int * a, b; // a - указатель на int, // но b - это просто int int * a, * b; // УЖАСНО: и a, и b являются указателями на целые числа / * write: * / int * a, * b;
Во избежание этой проблемы в часто задаваемых вопросах Бьярна Страуструпа рекомендуется объявлять только одну переменную на строку при использовании соглашения C ++.
Те же соображения применимы к определению ссылок и ссылок на rvalue:
int var = 22; int const refToConst = var; // ОК int const ref2 = var, ref3 = var; // ЗАБОТА: // ref2 - ссылка, а ref3 - нет: // ref3 - константа int, инициализированная // значением var int const constRef = var; // ОШИБКА: ссылки все равно не могут измениться. // C ++: int rref = int (5), value = 10; // ЗАБОТА: // rref - это ссылка на rvalue, но // значение - это просто int. / * запись: * / int rref = int (5), value = 10;
Более сложные объявления встречаются при использовании многомерных массивов и ссылок (или указателей) на указатели. Хотя иногда утверждают, что такие объявления сбивают с толку и подвержены ошибкам и поэтому их следует избегать или заменять структурами более высокого уровня, всегда можно использовать процедуру, описанную в верхней части этого раздела, без внесения двусмысленности или путаницы.
const
могут быть объявлены как в параметрах функции, так и в переменных (static или автоматически, включая глобальные или локальные). Интерпретация варьируется в зависимости от использования. Статическая переменная const
(глобальная переменная или статическая локальная переменная) является константой и может использоваться для таких данных, как математические константы, такие как double const PI = 3.14159
- реально длиннее, или общие параметры времени компиляции. Автоматическая переменная const
(нестатическая локальная переменная) означает, что происходит однократное присвоение, хотя каждый раз может использоваться другое значение, например int const x_squared = x * х
. Параметр const
в передаче по ссылке означает, что указанное значение не изменяется - оно является частью контракта - в то время как параметр const
в передаче - по значению (или сам указатель при передаче по ссылке) ничего не добавляет к интерфейсу (поскольку значение было скопировано), но указывает, что внутренне функция не изменяет локальную копию параметра (это это разовое задание). По этой причине некоторые предпочитают использовать const
в параметрах только для передачи по ссылке, где он изменяет контракт, но не для передачи по значению, где он раскрывает реализацию.
Чтобы воспользоваться преимуществами подхода дизайн по контракту для определяемых пользователем типов (структур и классов), которые могут имеют методы, а также данные-члены, программист может пометить методы экземпляра как const
, если они не изменяют элементы данных объекта. Таким образом, применение квалификатора const
к методам экземпляра является важной функцией для корректности констант и недоступно во многих других объектно-ориентированных языках, таких как Java и C # или в Microsoft C ++ / CLI или Управляемые расширения для C ++. В то время как методы const
могут быть вызваны как объектами const
, так и объектами, отличными от const
, методы не- const
могут быть вызваны только объектами, не const
объекты. Модификатор const
в методе экземпляра применяется к объекту, на который указывает указатель «this
», который является неявным аргументом, передаваемым всем методам экземпляра. Таким образом, наличие методов const
- это способ применить константную корректность к неявному аргументу указателя «this
», как и к другим аргументам.
Этот пример иллюстрирует:
класс C {int i; public: int Get () const // Обратите внимание на тег «const» {return i; } void Set (int j) // Обратите внимание на отсутствие «const» {i = j; }}; void Foo (C nonConstC, C const constC) {int y = nonConstC.Get (); // Хорошо int x = constC.Get (); // Хорошо: Get () - это const nonConstC.Set (10); // Хорошо: nonConstC можно изменить constC.Set (10); // Ошибка! Set () - это неконстантный метод, а constC - это объект с квалификацией const}
В приведенном выше коде неявный указатель «this
» на Set ()
имеет введите "C * const
"; тогда как указатель «this
» на Get ()
имеет тип «C const * const
», что указывает на то, что метод не может изменить свой объект через «этот
"указатель.
Часто программист предоставляет как const
, так и не- const
метод с одним и тем же именем (но, возможно, совершенно по-разному) в классе для размещения обоих типов. звонящих. Рассмотрим:
class MyArray {int data [100]; общедоступные: int Get (int я) {вернуть данные [я]; } int const Get (int i) const {вернуть данные [я]; }}; void Foo (MyArray array, MyArray const constArray) {// Получить ссылку на элемент массива // и изменить его значение, на которое оно ссылается. array.Get (5) = 42; // ОК! (Вызов: int MyArray :: Get (int)) constArray.Get (5) = 42; // Ошибка! (Вызовы: int const MyArray :: Get (int) const)}
const
-ость вызывающего объекта определяет, какая версия MyArray :: Get ()
будет быть вызванным и, таким образом, будет ли вызывающему лицу дана ссылка, с которой он может манипулировать или только наблюдать частные данные в объекте. Эти два метода технически имеют разные сигнатуры, потому что их указатели «this
» имеют разные типы, что позволяет компилятору выбрать правильный. (Возврат ссылки const
на int
вместо простого возврата int
по значению может быть излишним во втором методе, но тот же метод может быть используется для произвольных типов, как в стандартной библиотеке шаблонов.)
Есть несколько лазеек для константной корректности в C и C ++. Они существуют в первую очередь для совместимости с существующим кодом.
Первый, который применим только к C ++, - это использование const_cast
, которое позволяет программисту удалять квалификатор const
, делая любой объект изменяемым. Необходимость удаления квалификатора возникает при использовании существующего кода и библиотек, которые нельзя изменить, но которые не являются корректными с константой. Например, рассмотрим этот код:
// Прототип функции, которую мы не можем изменить, но которая //, как мы знаем, не изменяет переданный объект. Void LibraryFunc (int * ptr, int size); void CallLibraryFunc (int const * ptr, int size) {LibraryFunc (ptr, size); // Ошибка! Отбрасывает квалификатор const int * nonConstPtr = const_cast(ptr); // Удаляем квалификатор LibraryFunc (nonConstPtr, size); // OK}
Однако любая попытка изменить объект, который сам объявлен const
с помощью const cast, приводит к неопределенному поведению в соответствии со стандартом ISO C ++. В приведенном выше примере, если ptr
ссылается на глобальную, локальную переменную или переменную-член, объявленную как const
, или объект, выделенный в куче с помощью new int const
, код верен только в том случае, если LibraryFunc
действительно не изменяет значение, на которое указывает ptr
.
. Язык C нуждается в лазейке, потому что существует определенная ситуация. Переменные со статической продолжительностью хранения могут быть определены с начальным значением. Однако инициализатор может использовать только константы, такие как строковые константы и другие литералы, и не может использовать непостоянные элементы, такие как имена переменных, независимо от того, объявлены ли элементы инициализатора const
или нет, или статическая длительность объявляется переменная const
или нет. Существует непереносимый способ инициализировать переменную const
, которая имеет статическую продолжительность хранения. Тщательно сконструировав приведение типов в левой части более позднего присваивания, можно записать переменную const
, эффективно удаляя атрибут const
и «инициализируя» его непостоянным элементы, такие как другие переменные const
и т.п. Запись в переменную const
таким образом может работать, как задумано, но это вызывает неопределенное поведение и серьезно противоречит константной корректности:
size_t const bufferSize = 8 * 1024; size_t const userTextBufferSize; // начальное значение зависит от const bufferSize, здесь не может быть инициализировано... int setupUserTextBox (textBox_t * defaultTextBoxType, rect_t * defaultTextBoxLocation) {* (size_t *) userTextBufferSize = bufferSize - sizeof (struct textBoxControls); // предупреждение: может работать, но не гарантируется C...}
Другая лазейка применима как к C, так и к C ++. В частности, языки диктуют, что указатели и ссылки на элементы должны быть «неглубокими» по отношению к const
-нности их владельцев, то есть содержащий объект const
имеет все const
, за исключением того, что указатели на члены (и рефери) по-прежнему изменяемы. Для иллюстрации рассмотрим этот код C ++:
struct S {int val; int * ptr; }; void Foo (S const s) {int я = 42; s.val = i; // Ошибка: s - это константа, поэтому val - это константа int s.ptr = i; // Ошибка: s является константой, поэтому ptr - константный указатель на int * s.ptr = i; // ОК: данные, на которые указывает ptr, всегда изменяемы, // даже если это иногда нежелательно}
Хотя объект s
, переданный в Foo ()
, является постоянным, что делает все его члены постоянными, указатель, доступный через s.ptr
, по-прежнему можно изменять, хотя это может быть нежелательно с точки зрения const
-корректности, потому что s
может единолично владеть указателем. По этой причине Мейерс утверждает, что значение по умолчанию для указателей и ссылок на элементы должно быть "глубоким" const
-ness, которое может быть переопределено квалификатором изменяемым
, когда указатель не принадлежит контейнер, но эта стратегия создаст проблемы совместимости с существующим кодом. Таким образом, по историческим причинам эта лазейка остается открытой в C и C ++.
Последнюю лазейку можно закрыть с помощью класса, скрывающего указатель за const
-правильным интерфейсом, но такие классы либо не поддерживают обычную семантику копирования из const
(подразумевая, что содержащий класс не может быть скопирован с помощью обычной семантики) или разрешить другие лазейки, разрешив удаление const
-ности посредством непреднамеренного или преднамеренного копирования.
Наконец, несколько функций в стандартной библиотеке C нарушают константную корректность, поскольку они принимают указатель const
на символьную строку и возвращают не- const
указатель на часть той же строки. strtol
и strchr
входят в число этих функций. Некоторые реализации стандартной библиотеки C ++, такие как Microsoft, пытаются закрыть эту лазейку, предоставляя две перегруженные версии некоторых функций: версию «const
» и версию «не- . const
"версия.
Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам и, соответственно, подвергалось критике и не принималось за пределами узкого семейства C C, C ++ и D. Java и C #, на которые сильно повлияли C и C ++, оба явно отклонили квалификаторы типа const
, вместо этого выражая постоянство ключевыми словами, которые применяются к идентификатору (final
в Java, const
и только для чтения
в C #). Даже в C и C ++ использование const
значительно различается: одни проекты и организации используют его последовательно, а другие избегают.
strchr
проблемаКвалификатор типа const
вызывает трудности, когда логика функции не зависит от того, является ли ее вход постоянным или нет, но возвращает значение, которое должно быть того же квалифицированного типа, что и вход. Другими словами, для этих функций, если ввод является константой (с квалификатором const), возвращаемое значение также должно быть, но если вход является переменным (не с квалификацией const
), возвращаемое значение должно быть также. Поскольку сигнатура типа этих функций различается, для этого требуются две функции (или потенциально больше, в случае нескольких входов) с одинаковой логикой - форма общего программирования.
Эта проблема возникает даже для простых функций в стандартной библиотеке C, особенно strchr
; Ричи приписывает это наблюдение Тому Плему в середине 1980-х годов. Функция strchr
находит символ в строке; формально он возвращает указатель на первое вхождение символа c
в строке s
, а в классическом C (KR C) его прототипом является:
char * strchr ( char * s, int c);
Функция strchr
не изменяет входную строку, но возвращаемое значение часто используется вызывающим для изменения строки, например:
if (p = strchr (q, '/ ')) * p =' ';
Таким образом, с одной стороны, входная строка может быть const
(так как она не изменяется функцией), и если входная строка const
, возвращаемое значение должно быть как ну - проще всего потому, что он может возвращать точно входной указатель, если первый символ совпадает, - но, с другой стороны, возвращаемое значение не должно быть const
, если исходная строка не была const
, так как вызывающий может пожелать использовать указатель для изменения исходной строки.
В C ++ это делается с помощью перегрузки функции, обычно реализуемой с помощью шаблона , в результате чего создаются две функции, так что возвращаемое значение имеет то же const
-квалифицированный тип в качестве входных данных:
char * strchr (char * s, int c); char const * strchr (char const * s, int c);
Они, в свою очередь, могут быть определены шаблоном:
templateT * strchr (T * s, int c) {...}
В D это обрабатывается через inout
ключевое слово, которое действует как подстановочный знак для const, неизменяемого или неквалифицированного (переменной), что дает:
inout (char) * strchr (inout (char) * s, int c);
Однако в C ни то, ни другое невозможно, поскольку C не имеет перегрузки функций, и вместо этого это обрабатывается с помощью одной функции, где вход постоянен, но выход доступен для записи:
char * strchr (char const * s, int c);
Это позволяет использовать идиоматический код C, но удаляет квалификатор const, если ввод действительно был квалифицирован как const, что нарушает безопасность типов. Это решение было предложено Ричи и впоследствии принято. Это различие является одним из недостатков совместимости C и C ++.
В версии 2 языка программирования D существуют два ключевых слова, относящиеся к const. Ключевое слово неизменяемое
обозначает данные, которые нельзя изменить с помощью какой-либо ссылки. Ключевое слово const
обозначает неизменяемое представление изменяемых данных. В отличие от C ++ const
, D const
и immutable
являются «глубокими» или транзитивными, и все, что доступно через const
или неизменяемый
объект - это const
или неизменяемый
соответственно.
Пример константы и неизменяемости в D
int foo = new int [5]; // foo изменяемый. const int bar = foo; // bar - это константное представление изменяемых данных. неизменяемый int baz = foo; // Ошибка: все представления неизменяемых данных должны быть неизменными. неизменяемый int nums = новый неизменяемый (int) [5]; // Невозможно создать изменяемую ссылку на nums. const int constNums = числа; // Работает. immutable неявно преобразуется в const. int mutableNums = числа; // Ошибка: невозможно создать изменяемое представление неизменяемых данных.
Пример транзитивной или глубокой константы в D
class Foo {Foo next; int num; } неизменяемый Foo foo = новый неизменяемый (Foo); foo.next.num = 5; // Не компилируется. foo.next имеет тип неизменяемый (Foo). // foo.next.num имеет тип неизменяемый (int).
const
была представлена Бьярном Страуструпом в C с классами, предшественником C ++, в 1981 году, и первоначально называется только для чтения
. Что касается мотивации, Страуструп пишет:
Первое использование в качестве альтернативы макросам с заданной областью и типизацией аналогично выполнялось для макросов, подобных функциям, через ключевое слово inline
. Константные указатели и нотация * const
были предложены Деннисом Ритчи и приняты таким образом.
const
был затем принят в C как часть стандартизации и появляется в C89 (и последующие версии) вместе с другим квалификатором типа, volatile
. Еще один уточняющий термин, noalias
, был предложен на заседании комитета X3J11 в декабре 1987 года, но был отклонен; его цель в конечном итоге была достигнута с помощью ключевого слова restrict
в C99. Ричи не очень поддерживал эти дополнения, утверждая, что они не «несут их вес», но в конечном итоге не выступал за их удаление из стандарта.
Впоследствии D унаследовал const
от C ++, где он известен как конструктор типа (не квалификатор типа ), и добавлены два дополнительных конструктора типа, неизменяемый
и inout
, для обработки связанных вариантов использования.
Другие языки не следуют C / C ++ в том, что имеют постоянную часть типа, хотя они часто имеют внешне похожие конструкции и могут использовать ключевое слово const
. Обычно это используется только для констант (постоянных объектов).
В C # есть ключевое слово const
, но с радикально другой и более простой семантикой: оно означает константу времени компиляции и не является частью типа.
Nim имеет ключевое слово const
, аналогичное ключевому слову C #: оно также объявляет константу времени компиляции, а не является частью типа. Однако в Nim константу можно объявить из любого выражения, которое можно вычислить во время компиляции. В C # только встроенные типы C # могут быть объявлены как const
; пользовательские типы, включая классы, структуры и массивы, не могут быть const
.
Java не имеет const
- вместо этого он имеет final
, который может применяться к локальные "переменные" объявления и применяются к идентификатору, а не к типу. Он имеет другое объектно-ориентированное использование для членов объекта, которое является источником имени.
Спецификация языка Java рассматривает const
как зарезервированное ключевое слово, т. Е. Такое, которое нельзя использовать в качестве идентификатора переменной, но не присваивает ему семантику: это зарезервированное слово (не может быть используется в идентификаторах), но не в ключевом слове (не имеет особого значения). Считается, что ключевое слово было зарезервировано, чтобы позволить расширение языка Java включать методы const
в стиле C ++ и указатель на тип const
. An enhancement request ticket for implementing const
correctness exists in the Java Community Process, but was closed in 2005 on the basis that it was impossible to implement in a backwards-compatible fashion.
The contemporary Ada 83 independently had the notion of a constant object and a constant
keyword, with input parameters and loop parameters being implicitly constant. Here the constant
is a property of the object, not of the type.
JavaScript has a const
declaration that defines a block-scoped variable that cannot be reassigned nor redeclared. It defines a read-only reference to a variable that cannot be redefined, but in some situations the value of the variable itself may potentially change, such as if the variable refers to an object and a property of it is altered.