Почему этот цикл останавливается после первого прохода?

Extra Superfluous const плохо работает с точки зрения API:

Включение лишнего избыточного const в ваш код для параметров встроенного типа, передаваемых значением, загромождает ваш API, не делая никаких значимых обещаний пользователю или пользователю API (это только мешает реализации).

Слишком много «const» в API, когда это не нужно, похоже на «плачущий волк», в конце концов люди начнут игнорировать «const», потому что это повсюду и ничего не значит

Аргумент «reductio ad absurdum» для дополнительных констант в API хорош для этих первых двух точек, если бы было больше константных параметров, то каждый аргумент, который может иметь константу на это, СЛЕДУЕТ иметь const на нем. На самом деле, если бы это было действительно так хорошо, вы бы хотели, чтобы const был параметром по умолчанию для параметров и имел ключевое слово «mutable» только в том случае, если вы хотите изменить параметр.

Так что давайте попробуем вставить мы можем:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Рассмотрим приведенную выше строку кода. Декларация не только более захламлена, но и длиннее и труднее читать, но три из четырех ключевых слов «const» могут быть безопасно проигнорированы пользователем API. Однако дополнительное использование «const» сделало вторую линию потенциально ОПАСНЫМ!

Почему?

Быстрое неправильное считывание первого параметра char * const buffer может заставить вас думать, что он не будет изменять память в буфере данных, который передается, - однако это не правда! Избыточный «const» может привести к опасным и неправильным предположениям о вашем API при быстром сканировании или неправильном прочтении.


Сверхбыстрая константа неэффективна и с точки зрения реализации кода:

#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. Сторонники состязания утверждают, что это хорошо, потому что это позволяет помещать 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
}

Вся избыточная константа действительно делает код программиста менее удобочитаемым, заставляя его использовать другую локальную копию или функцию обертки, когда он хочет изменить переменную или передать переменную неконстантной ссылкой.

Посмотрите на этот пример. Что более читаемо? Очевидно ли, что единственной причиной дополнительной переменной во второй функции является то, что какой-то разработчик API бросил лишнюю константу?

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;
    }
}

Надеюсь, мы кое-что узнали. Излишняя константа - это зарисовка в области API, раздражающая наг, мелкое и бессмысленное обещание, ненужное препятствие и иногда приводит к очень опасным ошибкам.

0
задан JvdV 1 March 2019 в 22:33
поделиться