Слишком много лишних const плохо с точки зрения API:
Добавление в ваш код лишних констант для параметров внутреннего типа, передаваемых по значению, загромождает ваш API , не давая никаких значимых обещаний вызывающей стороне или пользователю API (это только мешает реализации).
Слишком много «const» в API, когда оно не нужно, похоже на « плачущего волка », в конце концов, люди начнут игнорировать «const», потому что он повсюду и ничего не значит в большинстве случаев.
Аргумент "reductio ad absurdum" к дополнительным концам в API хорош для этих первых двух пунктов: если больше параметров const хороши, то каждый аргумент, который может иметь const, ДОЛЖЕН иметь const на нем. На самом деле, если бы это было действительно так хорошо, вы бы хотели, чтобы const был параметром по умолчанию для параметров и имел бы ключевое слово типа «изменяемый» только тогда, когда вы хотите изменить параметр.
Итак, давайте попробуем положить в const, где мы можем:
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
Рассмотрим строку кода выше. Не только объявление более загромождено, длиннее и труднее для чтения, но и пользователь API может безопасно игнорировать три из четырех ключевых слов «const». Однако дополнительное использование const сделало вторую строку потенциально ОПАСНОЙ!
Почему?
Быстрое неверное прочтение первого параметра char * const buffer
может заставить вас подумать, что он не изменит память в буфере данных, который передается, однако это не так! Избыточное «const» может привести к опасным и неверным предположениям о вашем API при сканировании или неправильном прочтении.
Лишние const также плохи с точки зрения реализации кода:
#if FLEXIBLE_IMPLEMENTATION
#define SUPERFLUOUS_CONST
#else
#define SUPERFLUOUS_CONST const
#endif
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count);
Если FLEXIBLE_IMPLEMENTATION не соответствует действительности, то API «обещает» не реализовывать функцию первым способом ниже.
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
// Will break if !FLEXIBLE_IMPLEMENTATION
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
for(int i=0;i<count;i++)
{
dest[i]=source[i];
}
}
Это очень глупое обещание. Почему вы должны давать обещание, которое не приносит никакой пользы вашему абоненту и только ограничивает вашу реализацию?
Обе они являются совершенно правильными реализациями одной и той же функции, хотя все, что вы сделали, - это без необходимости связали одну руку за спиной.
Кроме того, это очень поверхностное обещание, которое легко (и юридически обойдется).
inline void bytecopyWrapped(char * dest,
const char *source, int count)
{
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source,SUPERFLUOUS_CONST int count)
{
bytecopyWrapped(dest, source, count);
}
Послушай, я все-таки реализовал это так, хотя обещал не делать этого, просто используя функцию-обертку. Это как когда плохой парень обещает не убивать кого-то в фильме и приказывает своему приспешнику убить их.
Эти лишние консты стоят не больше, чем обещание плохого парня из фильма.
Но способность лгать становится еще хуже
Я понял, что вы можете не соответствовать const в заголовке (объявление) и коде (определение) с помощью ложного const. Адвокаты, довольные константой, утверждают, что это хорошо, так как позволяет использовать констант только в определении.
// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
Тем не менее, обратное утверждение верно ... вы можете поместить ложный констант только в объявлении и игнорировать его в определении. Это только делает излишнее const в API более ужасной вещью и ужасной ложью - см. Этот пример:
class foo
{
void test(int * const pi);
};
void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
pi++; // I promised in my definition I wouldn't modify this
}
Все, что на самом деле делает лишнее const - это делает код разработчика менее читаемым, заставляя его использовать другую локальную копию или функцию-обертку, когда он хочет изменить переменную или передать переменную по неконстантной ссылке.
Посмотрите на этот пример. Что является более читабельным? Очевидно ли, что единственная причина для дополнительной переменной во второй функции заключается в том, что какой-то разработчик API добавил лишнее const?
struct llist
{
llist * next;
};
void walkllist(llist *plist)
{
llist *pnext;
while(plist)
{
pnext=plist->next;
walk(plist);
plist=pnext; // This line wouldn't compile if plist was const
}
}
void walkllist(llist * SUPERFLUOUS_CONST plist)
{
llist * pnotconst=plist;
llist *pnext;
while(pnotconst)
{
pnext=pnotconst->next;
walk(pnotconst);
pnotconst=pnext;
}
}
Надеюсь, мы кое-что узнали здесь. Лишний const - это загромождение API, раздражающее раздражение, поверхностное и бессмысленное обещание, ненужное препятствие и иногда приводящее к очень опасным ошибкам.