Есть ли недостатки в том, чтобы пометить все неизменяемые переменные как const?
Я много искал в интернете и нашел много информации о том, как помечать функции и их параметры как const
, но не нашел руководства по пометке переменных как const
.
Вот простой пример:
#include <string>
#include <iostream>
void example(const std::string& x) {
size_t length = x.length();
for (size_t i = 0; i < length; ++i) {
std::cout << x.at(i) << std::endl;
}
}
int main() {
example("hello");
}
Почему бы не сделать
size_t length = x.length();
константным, как
const size_t length = x.length();
по умолчанию?
Я понимаю, что такой простой пример не демонстрирует никаких значительных преимуществ, но это кажется полезным в большом коде, где можно случайно изменить переменную, которую не следовало бы изменять.
Несмотря на это преимущество, я не вижу, чтобы это часто использовалось (в тех C++ кодовых базах, которые я видел) или упоминалось так же часто, как пометка функций и их параметров как const
.
Существует ли какой-то минус в этом, кроме необходимости вводить 5 лишних символов? Я не нашел много информации по этому вопросу и не хочу навредить себе, если наличие такого количества const
является проблемой.
5 ответ(ов)
Отметка переменных, которые вы не собираетесь изменять, ключевым словом const
не имеет недостатков.
Есть некоторые преимущества: компилятор поможет вам обнаружить, когда вы случайно изменяете переменную, которую не должны изменять, и компилятор может (хотя из-за наличия const_cast
и mutable
это бывает редко) сгенерировать более оптимизированный код.
Поэтому я бы посоветовал использовать const
, где это возможно. Недостатков нет, а ваш компилятор может помочь вам выявить ошибки. Нет причин для отказа от этого (кроме немного большего объема ввода).
Имейте в виду, что это также касается и членских функций. Делайте их const
, когда это возможно — это позволяет использовать их в большем количестве контекстов и помогает пользователям лучше понимать код ("вызов этой функции не изменит объект" — это ценная информация).
Я могу выделить как минимум два недостатка:
- многословность: больше слов, больше символов для обработки и т.д.
- инерция: если вам нужно будет изменить код, вам придется удалить это
const
.
Тем не менее, оба этих недостатка стоят своих затрат.
Многословность — это часто приводимый аргумент против явности, однако люди зачастую путают скорость чтения со скоростью понимания. Безусловно, между многословностью и явностью важно найти баланс: слишком много слов может отвлекать от полезной информации, а слишком краткое и неявное выражение может не представлять ту информацию, которую необходимо восстанавливать или выводить из контекста.
Лично я использую типизированный язык с статической проверкой, чтобы компилятор выявлял мои ошибки как можно раньше; аннотирование с помощью const
передаёт информацию как читателю, так и компилятору. Я считаю это стоящим дополнительных 6 символов.
Что касается инерции, то удаление const
обычно является незначительной затратой при изменении... и это окупает себя, так как заставляет вас пройтись по всем местам, где он используется, и проверить окружающий код, чтобы убедиться, что действительно допустимо удалить этот const
. Вдруг изменение определенного элемента данных в коде, где он ранее был неизменяемым, требует проверки, не полагалась ли какая-либо часть кода (или её вызывающие функции) на это свойство неизменяемости.
Для локальной переменной size_t length
в таком коротком методе, как этот, не имеет большого значения, использовать ли const
или нет. Недостаток в виде излишней многословности в основном компенсируется относительной безопасностью, позволяя избежать опечаток, случайно изменяющих длину. Делайте так, как подсказывает ваш локальный стиль кодирования или ваше собственное внутреннее чувство.
Для более длинного или сложного метода ситуация может быть другой. Но, с другой стороны, если ваш метод настолько сложен, что это важно, возможно, имеет смысл рассмотреть возможность рефакторинга вашего кода на более простые части. В любом случае, если вы читаете и понимаете код, дополнительный намек, предоставляемый явным const
, становится в какой-то степени несущественным — приятно, но не обязательно.
Немного связано, хотя вы об этом не спрашивали: для параметра-ссылки вашего метода example
вам определённо нужен const
, потому что вам может потребоваться передать ему константную строку. Если вы хотите отключить возможность передачи константной строки (потому что думаете, что добавите код для её изменения), тогда const
там можно опустить.
Проблема в том, что такое "переопределение" переменной на практике почти никогда не происходит.
С другой стороны, const
— это болезнь, которая распространяется по вашему коду, как чума. Как только вы объявляете переменную как const
, все операции с ней также должны быть const
, и они могут вызывать только const
функции. Это становится бесконечным циклом.
В большинстве случаев использование const
не стоит тех усилий и ограничений, которые вы за него платите. Есть лишь несколько ситуаций, где const
действительно приносит защиту (например, при использовании ключей в множествах), но даже в этих случаях можно спорить, что нужно быть полным идиотом, чтобы не заметить потенциальные проблемы изначально. В итоге это не оправдывает все языковые ограничения, постоянное дублирование кода и избыточную металогическую сложность.
const
— это хорошая идея, которая кажется привлекательной в теории, но практическая реальность такова, что const
— это полная трата времени и ресурсов. Сжигайте это огнем.
Я согласен с большинством ответов, данных до настоящего момента, однако некоторые аспекты все еще остаются недооцененными.
При определении интерфейсов ключевое слово const
может оказаться очень полезным. Но следует помнить, что у него есть свои ограничения и порой оно может создавать проблемы - это тот недостаток, который я бы отметил.
Давайте еще раз внимательно взглянем на вопрос:
Есть ли какие-либо недостатки в том, чтобы пометить все переменные, которые вы не изменяете, как
const
?
Если вы наблюдаете, что не изменяете что-то, вы можете сказать, что это по сути константа. Инструменты анализа кода могут это определить, и даже ваш компилятор уже будет это знать. Но это наблюдение не должно вызывать у вас рефлекса добавления const.
Лучше задумайтесь о самой переменной, задайте себе вопросы:
- Является ли она вообще полезной?
- Предназначена ли она быть константой?
Иногда промежуточную переменную можно просто убрать, а иногда для улучшения кода необходимо небольшое изменение (добавление или удаление функции).
Добавление ключевого слова const
может защитить ваш код от ошибок в других местах, но также может создать проблемы при изменениях в месте объявления.
Также хочу сказать пару слов о членах класса, которые не изменяются. Если вы решите объявить член класса как const
, вы должны инициализировать его в списке инициализации конструктора содержащего класса. Это увеличивает пред условия для создания объектов этого класса.
Так что не добавляйте const
везде, где это позволяет ваш компилятор. Не поддавайтесь искушению "окаменеть ваш код" 😉
Разница между const int*, const int * const и int * const?
В чем разница между constexpr и const?
Что означает "const" в конце объявления функции?
Влияет ли использование const на параметры функции? Почему это не сказывается на сигнатуре функции?
Как получить размер std::vector в формате int?