В некоторых языках программирования const — это квалификатор типа ( ключевое слово, применяемое к типу данных ), который указывает, что данные доступны только для чтения. Хотя это может использоваться для объявления констант , const в семействе языков C отличается от аналогичных конструкций в других языках тем, что является частью типа и, таким образом, имеет сложное поведение при сочетании с указателями , ссылками, составными типами данных и проверкой типов . В других языках данные не находятся в одном месте памяти , а копируются во время компиляции для каждого использования. [1] Языки, которые его используют, включают C , C++ , D , JavaScript , Julia и Rust .
При применении в объявлении объекта [a] он указывает, что объект является константой : его значение не может быть изменено, в отличие от переменной . Это базовое использование — для объявления констант — имеет параллели во многих других языках.
Однако, в отличие от других языков, в семействе языков C const
является частью типа , а не частью объекта . Например, в C объявляет объект типа – является частью типа, как если бы он был проанализирован как "(int const) x" – в то время как в Ada объявляет константу (вид объекта) типа : является частью объекта , но не частью типа .int const x = 1;
x
int const
const
X : constant INTEGER := 1_
X
INTEGER
constant
Это имеет два тонких результата. Во-первых, const
может применяться к частям более сложного типа — например, int const * const x;
объявляет постоянный указатель на постоянное целое число, в то время как int const * x;
объявляет переменный указатель на постоянное целое число и int * const x;
объявляет постоянный указатель на переменное целое число. Во-вторых, поскольку const
является частью типа, он должен соответствовать как часть проверки типа. Например, следующий код недопустим:
void f ( int & x ); // ... int const i ; f ( i );
потому что аргумент f
должен быть переменным целым числом, но i
является постоянным целым числом. Это соответствие является формой корректности программы и известно как const-корректность . Это допускает форму программирования по контракту , где функции указывают как часть своей сигнатуры типа , изменяют ли они свои аргументы или нет, и является ли их возвращаемое значение модифицируемым или нет. Эта проверка типов в первую очередь представляет интерес для указателей и ссылок — не базовых типов значений, таких как целые числа, — но также для составных типов данных или шаблонных типов, таких как контейнеры . Она скрыта тем фактом, что const
часто может быть опущена из-за приведения типов (неявного преобразования типов ) и того, что C является вызовом по значению (C++ и D являются либо вызовом по значению, либо вызовом по ссылке).
Идея константности не подразумевает, что переменная, как она хранится в памяти компьютера , не подлежит записи. Скорее, const
-ность — это конструкция времени компиляции , которая указывает, что программист должен делать, а не обязательно то, что он может делать. Обратите внимание, однако, что в случае предопределенных данных (таких как char const *
строковые литералы ) C const
часто не подлежит записи.
В то время как константа не меняет своего значения во время работы программы, объявленный объект const
действительно может изменить свое значение во время работы программы. Типичным примером являются регистры только для чтения во встроенных системах, такие как текущее состояние цифрового входа. Регистры данных для цифровых входов часто объявляются как const
и volatile
. Содержимое этих регистров может изменяться без каких-либо действий со стороны программы ( volatile
), но было бы неправильно, если бы программа пыталась записать в них ( const
).
Кроме того, (нестатическая) функция-член может быть объявлена как const
. В этом случае this
указатель внутри такой функции имеет тип object_type const *
, а не просто тип object_type *
. [2] Это означает, что неконстантные функции для этого объекта не могут быть вызваны изнутри такой функции, а переменные-члены не могут быть изменены. В C++ переменная-член может быть объявлена как mutable
, указывая, что это ограничение к ней не применяется. В некоторых случаях это может быть полезно, например, при кэшировании , подсчете ссылок и синхронизации данных . В этих случаях логическое значение (состояние) объекта не изменяется, но объект не является физически постоянным, поскольку его побитовое представление может измениться.
В C, C++ и D все типы данных, включая те, которые определены пользователем, могут быть объявлены const
, а const-корректность диктует, что все переменные или объекты должны быть объявлены как таковые, если только их не нужно изменять. Такое упреждающее использование const
делает значения «более простыми для понимания, отслеживания и рассуждения» [3] , и, таким образом, повышает читаемость и понятность кода и упрощает работу в командах и поддержку кода, поскольку передает информацию о предполагаемом использовании значения. Это может помочь компилятору, а также разработчику при рассуждении о коде. Это также может позволить оптимизирующему компилятору генерировать более эффективный код. [4]
Для простых типов данных без указателей применение const
квалификатора просто. Он может располагаться с любой стороны некоторых типов по историческим причинам (например, const char foo = 'a';
эквивалентно char const foo = 'a';
). В некоторых реализациях использование const
дважды (например, const char const
или char const const
) генерирует предупреждение, но не ошибку.
Для указателей и ссылочных типов значение const
более сложное – либо сам указатель, либо значение, на которое указывается, либо и то, и другое может быть const
. Кроме того, синтаксис может быть запутанным. Указатель может быть объявлен как const
указатель на записываемое значение, или как записываемый указатель на const
значение, или const
как указатель на const
значение.Указатель const
не может быть переназначен для указания на другой объект, нежели тот, который ему изначально назначен, но его можно использовать для изменения значения, на которое он указывает (называемого pointee ) . [5] [6] [7] [8] [9] Ссылочные переменные в C++ являются альтернативным синтаксисом для const
указателей. Указатель на const
объект, с другой стороны, может быть переназначен для указания на другую область памяти (которая должна быть объектом того же типа или конвертируемого типа), но его нельзя использовать для изменения памяти, на которую он указывает. const
Указатель на const
объект также может быть объявлен и не может ни использоваться для изменения apointee, ни переназначаться для указания на другой объект. Следующий код иллюстрирует эти тонкости:
void Foo ( int * ptr , int const * ptrToConst , int * const constPtr , int const * const constPtrToConst ) { * ptr = 0 ; // OK: изменяет указанные данные ptr = NULL ; // OK: изменяет указатель * ptrToConst = 0 ; // Ошибка! Невозможно изменить указанные данные ptrToConst = NULL ; // OK: изменяет указатель * constPtr = 0 ; // OK: изменяет указанные данные 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
is a int const *
» (указатель является указателем на постоянное целое число). Таким образом:
int * ptr ; // *ptr — это значение типа int int const * ptrToConst ; // *ptrToConst — это константа (int: целое значение) int * const constPtr ; // constPtr — это константа (int *: указатель на целое число) int const * const constPtrToConst ; // constPtrToConst — это указатель-константа, указывающий // на постоянное значение
Следуя соглашению C++ об анализе типа, а не значения, эмпирическое правило заключается в том, чтобы читать объявление справа налево. Таким образом, все, что слева от звезды, может быть идентифицировано как указанный тип, а все, что справа от звезды, является свойствами указателя. Например, в нашем примере выше int const *
может быть прочитано как записываемый указатель, который ссылается на не записываемое целое число, и int * const
может быть прочитано как незаписываемый указатель, который ссылается на записываемое целое число.
Более общее правило, которое поможет вам понять сложные объявления и определения, работает следующим образом:
Вот пример:
При чтении слева важно читать элементы справа налево. Таким образом, an int const *
становится указателем на const int , а не const указателем на int .
В некоторых случаях C/C++ позволяет const
размещать ключевое слово слева от типа. Вот несколько примеров:
const int * ptrToConst ; //идентично: int const *ptrToConst, const int * const constPtrToConst ; //идентично: int const *const constPtrToConst
Хотя C/C++ допускает такие определения (которые близко соответствуют английскому языку при чтении определений слева направо), компилятор все равно читает определения согласно вышеупомянутой процедуре: справа налево. Но размещение const
перед тем, что должно быть константой, быстро вносит несоответствия между тем, что вы намереваетесь написать, и тем, что компилятор решает, что вы написали. Рассмотрим указатели на указатели:
int ** ptr ; // указатель на указатель на целые числа int const ** ptr // указатель на указатель на константное значение целого числа // (не указатель на константный указатель на целые числа) int * const * ptr // указатель на константный указатель на значения целых чисел // (не константный указатель на указатель на целые числа) int ** const ptr // константный указатель на указатели на целые числа // (ptr, идентификатор, будучи константой, не имеет смысла) int const ** const ptr // константный указатель на указатели на константные значения целых чисел
В качестве последнего замечания относительно определений указателей: всегда пишите символ указателя (*) как можно правее. Присоединение символа указателя к типу — сложная задача, поскольку он явно предполагает тип указателя, что не соответствует действительности. Вот несколько примеров:
int * a ; /* запись: */ int * a ; // a — указатель на int int * a , b ; // ЗАПУТАННОСТЬ /* запись: */ int * a , b ; // a — указатель на int, // но b — это просто int int * a , * b ; // УРОДЛИВОСТЬ: и a, и b — указатели на int /* запись: */ int * a , * b ;
В разделе часто задаваемых вопросов Бьярна Страуструпа рекомендуется объявлять только одну переменную на строку при использовании соглашения C++, чтобы избежать этой проблемы. [10]
Те же соображения применимы к определению ссылок и ссылок rvalue:
int var = 22 ; int const & refToConst = var ; // OK int const & ref2 = var , ref3 = var ; // ЗАПУТАННОСТЬ: // ref2 — это ссылка, а ref3 — нет: // ref3 — это константа int, инициализированная // значением var int & const constRef = var ; // ОШИБКА: так как ссылки в любом случае не могут изменяться. // C++: int && rref = int ( 5 ), value = 10 ; // ЗАПУТАННОСТЬ: // rref — это ссылка rvalue, но value — это // просто int. /* запись: */ int && rref = int ( 5 ), value = 10 ;
Более сложные объявления встречаются при использовании многомерных массивов и ссылок (или указателей) на указатели. Хотя иногда утверждается [ who? ], что такие объявления запутанны и подвержены ошибкам, и что поэтому их следует избегать или заменять структурами более высокого уровня, процедура, описанная в начале этого раздела, всегда может использоваться без внесения двусмысленностей или путаницы.
const
может быть объявлен как в параметрах функции, так и в переменных ( статических или автоматических, включая глобальные или локальные). Интерпретация различается в зависимости от использования. const
Статическая переменная (глобальная переменная или статическая локальная переменная) является константой и может использоваться для таких данных, как математические константы, такие как double const PI = 3.14159
– реалистично более длинные, или общие параметры времени компиляции. const
Автоматическая переменная (нестатическая локальная переменная) означает, что происходит единичное присваиваниеint const x_squared = x * x
, хотя каждый раз может использоваться другое значение, например . const
Параметр в передаче по ссылке означает, что указанное значение не изменяется – оно является частью контракта – в то время как const
параметр в передаче по значению (или сам указатель в передаче по ссылке) ничего не добавляет к интерфейсу (поскольку значение было скопировано), но указывает, что внутренне функция не изменяет локальную копию параметра (это единичное присваивание). По этой причине некоторые предпочитают использовать const
параметры только для передачи по ссылке, где он изменяет контракт, но не для передачи по значению, где он раскрывает реализацию.
Чтобы воспользоваться преимуществами подхода проектирования по контракту для определяемых пользователем типов (структур и классов), которые могут иметь методы, а также данные-члены, программист может пометить методы экземпляра так, как const
будто они не изменяют данные-члены объекта. const
Таким образом, применение квалификатора к методам экземпляра является существенной функцией для константной корректности и недоступно во многих других объектно-ориентированных языках, таких как Java и C# или в Microsoft C++/CLI или Managed Extensions для C++ . В то время как const
методы могут вызываться как объектами, так const
и не- const
объектами, не- const
методы могут вызываться только не- const
объектами. const
Модификатор метода экземпляра применяется к объекту, на который указывает this
указатель " ", который является неявным аргументом, передаваемым всем методам экземпляра. Таким образом, наличие const
методов является способом применения константной корректности к неявному this
аргументу указателя " ", как и к другим аргументам.
Этот пример иллюстрирует:
class 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() является константой nonConstC . Set ( 10 ); // Ok: nonConstC можно изменять constC . Set ( 10 ); // Ошибка! Set() — неконстантный метод, а constC — константный объект }
В приведенном выше коде неявный this
указатель " " на Set()
имеет тип " C *const
"; тогда как this
указатель " " на Get()
имеет тип " C const *const
", что указывает на то, что метод не может изменять свой объект через this
указатель " ".
Часто программист предоставляет как метод, так const
и не- const
метод с одинаковым именем (но, возможно, совершенно разным использованием) в классе, чтобы удовлетворить оба типа вызывающих. Рассмотрим:
class MyArray { int data [ 100 ]; public : int & Get ( int i ) { return data [ i ]; } int const & Get ( int i ) const { return data [ i ]; } }; 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 ) }
-ness const
вызывающего объекта определяет, какая версия MyArray::Get()
будет вызвана и, таким образом, будет ли вызывающему предоставлена ссылка, с помощью которой он может манипулировать или только наблюдать за закрытыми данными в объекте. Технически эти два метода имеют разные сигнатуры, поскольку их this
указатели " " имеют разные типы, что позволяет компилятору выбрать правильный. (Возврат const
ссылки на int
, вместо простого возврата int
по значению, может быть излишним во втором методе, но тот же прием можно использовать для произвольных типов, как в Standard Template Library .)
В C и C++ есть несколько лазеек к чистой константной корректности. Они существуют в основном для совместимости с существующим кодом.
Первый, который применим только к C++, — это использование const_cast
, которое позволяет программисту удалить const
квалификатор, сделав любой объект модифицируемым. Необходимость удаления квалификатора возникает при использовании существующего кода и библиотек, которые нельзя изменить, но которые не являются const-корректными. Например, рассмотрим этот код:
// Прототип для функции, которую мы не можем изменить, но которая, как мы знаем, // не изменяет переданный указатель. void LibraryFunc ( int * ptr , int size ); void CallLibraryFunc ( int const * ptr , int size ) { LibraryFunc ( ptr , size ); // Ошибка! Удаляет квалификатор const int * nonConstPtr = const_cast < int *> ( ptr ); // Удалить квалификатор LibraryFunc ( nonConstPtr , size ); // OK }
Однако любая попытка изменить объект, который сам объявлен const
посредством приведения типа const, приводит к неопределенному поведению согласно стандарту 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 ... }
Другая лазейка [11] применима как к C, так и к C++. В частности, языки диктуют, что указатели и ссылки на члены являются «поверхностными» по отношению к -ности const
их владельцев – то есть, содержащий объект, который const
имеет все const
члены, за исключением тех, что указатели членов (и рефери) остаются изменяемыми. Для иллюстрации рассмотрим этот код C++:
структура S { int val ; int * ptr ; }; void Foo ( S const & s ) { int i = 42 ; s . val = i ; // Ошибка: s является константой, поэтому val является константой int s . ptr = & i ; // Ошибка: s является константой, поэтому ptr является константным указателем на int * s . ptr = i ; // OK: данные, на которые указывает ptr, всегда изменяемы, // хотя иногда это нежелательно }
s
Хотя переданный объект Foo()
является константой, что делает все его члены константами, указатель, доступный через него, s.ptr
все еще может быть изменен, хотя это может быть нежелательно с точки зрения const
-корректности, поскольку s
может единолично владеть указателем. По этой причине Мейерс утверждает, что по умолчанию для указателей и ссылок на члены должна быть "глубокая" const
-ность, которая может быть переопределена квалификатором mutable
, когда указатель не принадлежит контейнеру, но эта стратегия создаст проблемы совместимости с существующим кодом. Таким образом, по историческим причинам [ требуется цитата ] эта лазейка остается открытой в C и C++.
Последнюю лазейку можно закрыть, используя класс для сокрытия указателя за const
-корректным интерфейсом, но такие классы либо не поддерживают обычную семантику копирования из const
объекта (подразумевая, что содержащий их класс также не может быть скопирован с помощью обычной семантики), либо допускают другие лазейки, разрешая удаление -ности const
посредством непреднамеренного или преднамеренного копирования.
Наконец, несколько функций в стандартной библиотеке C нарушают const-корректность до C23 , поскольку они принимают const
указатель на строку символов и возвращают не- const
указатель на часть той же строки. strstr
и strchr
входят в число этих функций. Некоторые реализации стандартной библиотеки C++, такие как [12] от Microsoft, пытаются закрыть эту лазейку, предоставляя две перегруженные версии некоторых функций: const
версию " " и "не- const
" версию.
Использование системы типов для выражения постоянства приводит к различным сложностям и проблемам, и соответственно подвергалось критике и не было принято за пределами узкого семейства C, состоящего из C, C++ и D. Java и C#, которые находятся под сильным влиянием C и C++, явно отвергли const
квалификаторы типов в стиле -, вместо этого выражая постоянство ключевыми словами, которые применяются к идентификатору ( final
в Java const
и readonly
в C#). Даже в C и C++ использование const
значительно различается, некоторые проекты и организации используют его последовательно, а другие избегают.
Квалификатор типа const
вызывает трудности, когда логика функции не зависит от того, является ли ее вход константой или нет, но возвращает значение, которое должно быть того же квалифицированного типа, что и вход. Другими словами, для этих функций, если вход константа (квалифицирована const), возвращаемое значение должно быть таким же, но если вход переменный (не const
квалифицирован -), возвращаемое значение должно быть таким же. Поскольку сигнатура типа этих функций различается, требуются две функции (или потенциально больше, в случае нескольких входов) с одинаковой логикой — форма обобщенного программирования .
Эта проблема возникает даже для простых функций в стандартной библиотеке C, в частности strchr
; это наблюдение приписывается Ритчи Тому Пламу в середине 1980-х годов. [13] Функция strchr
находит символ в строке; формально она возвращает указатель на первое вхождение символа c
в строку s
, а в классическом C (K&R C) ее прототипом является:
char * strchr ( char * s , int c );
Функция strchr
не изменяет входную строку, но возвращаемое значение часто используется вызывающей стороной для изменения строки, например:
если ( p = strchr ( q , '/' )) * p = ' ' ;
Таким образом, с одной стороны, входная строка может быть const
(поскольку она не изменяется функцией), и если входная строка является, const
то возвращаемое значение также должно быть — в основном потому, что оно может вернуть именно указатель ввода, если первый символ совпадает, — но с другой стороны, возвращаемое значение не должно быть, const
если исходная строка не была const
, поскольку вызывающая сторона может захотеть использовать указатель для изменения исходной строки.
В C++ это делается с помощью перегрузки функций , обычно реализуемой с помощью шаблона , что приводит к двум функциям, так что возвращаемое значение имеет тот же const
-квалифицированный тип, что и входные данные: [b]
char * strchr ( char * s , int c ); char const * strchr ( char const * s , int c );
Их, в свою очередь, можно определить с помощью шаблона:
шаблон < T > T * strchr ( T * s , int c ) { ... }
В языке D это обрабатывается с помощью inout
ключевого слова, которое действует как подстановочный знак для константы, неизменяемого или неквалифицированного (переменного), что дает: [14] [c]
inout ( char )* strchr ( inout ( char )* s , int c );
Однако в языке C ни один из этих вариантов невозможен, поскольку в нем нет перегрузки функций, и вместо этого эта проблема решается с помощью одной функции, где входные данные являются константой, а выходные данные доступны для записи:
char * strchr ( char const * s , int c );
Это позволяет использовать идиоматический код C, но удаляет квалификатор const, если входные данные на самом деле были квалифицированы const, нарушая безопасность типов. Это решение было предложено Ритчи и впоследствии принято. Это различие является одним из недостатков совместимости C и C++ .
Начиная с C23 , эта проблема решается с помощью универсальных функций. strchr
А другие функции, затронутые этой проблемой, будут возвращать const
указатель, если им был передан указатель, и неквалифицированный указатель, если им был передан неквалифицированный указатель. [15]
В версии 2 языка программирования D существуют два ключевых слова, относящихся к const. [16] Ключевое immutable
слово обозначает данные, которые не могут быть изменены посредством какой-либо ссылки. Ключевое const
слово обозначает неизменяемое представление изменяемых данных. В отличие от C++ const
, D const
и immutable
являются «глубокими» или транзитивными , и все, что достижимо посредством объекта const
или, immutable
является const
или immutable
соответственно.
Пример const и immutable в D
int [] foo = new int [ 5 ]; // foo является изменяемыми. const int [] bar = foo ; // bar является константным представлением изменяемыми данными. immutable int [] baz = foo ; // Ошибка: все представления неизменяемых данных должны быть неизменяемыми. immutable int [] nums = new immutable ( int )[ 5 ]; // Невозможно создать изменяемую ссылку на nums. const int [] constNums = nums ; // Работает. immutable неявно преобразуется в const. int [] mutableNums = nums ; // Ошибка: невозможно создать изменяемое представление неизменяемых данных.
Пример транзитивной или глубокой константы в D
класс Foo { Foo следующий ; int num ; } immutable Foo foo = new immutable ( Foo ); foo . next . num = 5 ; // Не скомпилируется. foo.next имеет тип immutable(Foo). // foo.next.num имеет тип immutable(int).
const
был представлен Бьярне Страуструпом в C with Classes , предшественнике C++ , в 1981 году и изначально назывался readonly
. [17] [18] Что касается мотивации, Страуструп пишет: [18]
Первое использование, как ограниченная и типизированная альтернатива макросам, было аналогично выполнено для макросов, подобных функциям, через inline
ключевое слово. Константные указатели и * const
нотация были предложены Деннисом Ритчи и приняты. [18]
const
затем был принят в C как часть стандартизации и появляется в C89 (и последующих версиях) вместе с другим квалификатором типа, volatile
. [19] Еще один квалификатор, noalias
, был предложен на заседании комитета X3J11 в декабре 1987 года, но был отклонен; его цель в конечном итоге была достигнута restrict
ключевым словом в C99 . Ритчи не очень поддерживал эти дополнения, утверждая, что они «не несут своего веса», но в конечном итоге не выступал за их удаление из стандарта. [20]
D впоследствии унаследовал const
от C++, где он известен как конструктор типа (а не квалификатор типа ), и добавил два дополнительных конструктора типа, immutable
и inout
, для обработки связанных вариантов использования. [d]
Другие языки не следуют C/C++ в том, что касается константности как части типа, хотя они часто имеют внешне похожие конструкции и могут использовать const
ключевое слово. Обычно это используется только для констант (константных объектов).
В C# есть const
ключевое слово, но с радикально иной и более простой семантикой: оно означает константу времени компиляции и не является частью типа.
Nim имеет const
ключевое слово, похожее на ключевое слово C#: оно также объявляет константу времени компиляции, а не формирует часть типа. Однако в Nim константа может быть объявлена из любого выражения, которое может быть вычислено во время компиляции. [21] В C# только встроенные типы C# могут быть объявлены как const
; определяемые пользователем типы, включая классы, структуры и массивы, не могут быть const
. [22]
В Java нет const
– вместо этого есть final
, который может применяться к локальным объявлениям «переменных» и применяется к идентификатору , а не к типу. У него есть другое объектно-ориентированное использование для членов объекта, что и является источником названия.
Спецификация языка Java рассматривает const
как зарезервированное ключевое слово – то есть такое, которое не может использоваться как идентификатор переменной – но не назначает ему семантики: это зарезервированное слово (его нельзя использовать в идентификаторах), но не ключевое слово (у него нет специального значения). Ключевое слово было включено как средство для компиляторов Java обнаруживать и предупреждать о неправильном использовании ключевых слов C++. [23] Билет запроса на улучшение для реализации const
корректности существует в Java Community Process , но был закрыт в 2005 году на основании того, что его было невозможно реализовать обратно совместимым образом. [24]
Современная Ada 83 независимо имела понятие постоянного объекта и constant
ключевого слова, [25] [e] с входными параметрами и параметрами цикла, которые были неявно постоянными. Здесь constant
является свойством объекта, а не типа.
В JavaScript есть const
объявление, которое определяет переменную с областью действия блока , которую нельзя ни переназначить, ни переобъявить. Оно определяет ссылку только для чтения на переменную, которую нельзя переопределить, но в некоторых ситуациях значение самой переменной может потенциально измениться, например, если переменная ссылается на объект, а его свойство изменено. [26]
const
является частью самого внешнего производного типа в объявлении; указатели усложняют обсуждение.char *s
это стандарт, а в C++ char* s
это стандарт.shared
конструктор типов, но он относится к вариантам использования volatile
, а не const
.Тип
в функции-члене, тип которой имеет
cv-qualifier-seq cv
и класс которой является
"указатель на
cv
".
this
X
X