Слишком много лишних констант плохо с точки зрения API:
Добавление в ваш код лишних констант для параметров внутреннего типа, передаваемых по значению , загромождает ваш API , не давая никаких значимых обещаний вызывающей стороне или пользователю API (это только мешает реализации).
Слишком много «const» в API, когда оно не нужно, похоже на « crying wolf », в конце концов, люди начнут игнорировать «const», потому что оно повсеместно и в большинстве случаев ничего не значит.
Аргумент "reductio ad absurdum" для дополнительных const в 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, раздражающее раздражение, поверхностное и бессмысленное обещание, ненужное препятствие и иногда приводящее к очень опасным ошибкам.