Не из лекции
На первом занятии эта тема не разбиралась.
constпоявляется в лонгриде ровно один раз — в примере сравнения дробных чисел, без объяснения. Страница написана «на вырост»: правила именования констант требует кодстайл, так что знать это полезно уже сейчас.
const — «после инициализации не меняется». constexpr — «значение известно
уже на этапе компиляции». Второе сильнее первого: всякая constexpr-переменная
является и const.
const int size = ReadSize(); // значение узнаем во время работы, менять нельзя
constexpr int kMaxSize = 100; // известно компиляторуЗачем
Читателю. const у переменной сообщает: дальше по коду её значение
не изменится, можно не выискивать присваивания.
Компилятору. constexpr можно использовать там, где требуется константное
выражение: размер массива, параметр шаблона, case в switch.
constexpr int kBufferSize = 256;
int buffer[kBufferSize]; // так можноСебе. Именованная константа вместо числа посреди выражения — это ещё и объяснение, что число означает:
constexpr int kSecondsInHour = 3600;
int hours = total_seconds / kSecondsInHour; // понятно без комментарияТакие «голые» числа называют магическими (magic numbers), и clang-tidy
на них ругается проверкой readability-magic-numbers. Исключение — 0 и 1.
Именование
По кодстайлу курса константы пишутся в kCamelCase:
kMaxSize, kSecondsInMinute, kEpsilon. Префикс k — от немецкого
Konstante, прижился через Google Style Guide. Правило распространяется
на глобальные константы, константы пространства имён и статические
константные поля класса.
const-корректность
Привычка помечать const всё, что не меняется, называется const-correctness.
Проверка misc-const-correctness в clang-tidy предлагает добавить const
к переменным, которые вычисляются один раз:
const int total_time = hours * kSecondsInHour + minutes * kSecondsInMinute;Дальше по коду видно: это значение окончательное.
Тонкости
constу переменной не означает, что значение известно заранее: оно может прийти изstd::cin.constexprтребует, чтобы инициализатор вычислялся компилятором, — вызов обычной функции туда не поставить.- Для указателей важно, что именно константа:
const int* p— нельзя менять значение по указателю,int* const p— нельзя менять сам указатель. Это отдельная тема, всплывёт вместе с указателями. constexprбывает и у функций, тогда их можно вычислять на этапе компиляции.
Частые ошибки
- Объявить константу без
kи получить замечание на ревью. - Магическое число «на один раз» — оно почти всегда встречается ещё раз, и тогда одно из мест забывают поправить.
- Ждать от
constзащиты от изменения через другой указатель или ссылку:const— договорённость, а не охрана памяти.
Углубление — L03
Обещанная тема с указателями разобрана в L03. Константным бывает и объект, на который указывают, и сам указатель:
const int* px = ...; // указатель на константу
int const* py = ...; // то же самое
int* const pz = ...; // константный указатель
const int* const pa = ...; // константный указатель на константуТипы указателей читаются справа налево. Указатель на константу можно взять и
от неконстантного объекта — тогда его нельзя менять только через этот
указатель. Обратное запрещено: float* py = &y при const float y —
ошибка компиляции, иначе константу можно было бы изменить через указатель.
Подробно — Указатель.