Подтвердить что ты не робот

Имеет ли смысл сделать фундаментальный (не указательный) параметр const?

Недавно я имел обмен с другим разработчиком С++ о следующем использовании const:

void Foo(const int bar);

Он считал, что использование const таким образом было хорошей практикой.

Я утверждал, что он ничего не делает для вызывающей функции (поскольку копия аргумента должна была быть передана, нет дополнительной гарантии безопасности в отношении перезаписи). Кроме того, это не позволяет разработчику Foo изменять свою частную копию аргумента. Таким образом, он как мандаты, так и рекламирует детали реализации.

Не конец света, но, конечно же, не что-то, что можно рекомендовать в качестве хорошей практики.

Мне любопытно, что другие думают по этому поводу.

Edit:

Хорошо, я не понимал, что константа аргументов не учитывает подпись функции. Таким образом, можно отметить аргументы как const в реализации (.cpp), а не в заголовке (.h) - и компилятор в порядке с этим. В этом случае я полагаю, что политика должна быть одинаковой для создания локальных переменных const.

Можно было бы утверждать, что наличие разных поисковых сигнатур в заголовке и исходном файле будет путать других (так как это меня смутило бы). Хотя я стараюсь следовать Принципу наименьшего удивления с тем, что я пишу, я думаю, разумно ожидать, что разработчики признают это законным и полезным.

4b9b3361

Ответ 1

Помните шаблон if(NULL == p)?

Есть много людей, которые скажут "вы должны написать такой код":

if(NULL == myPointer) { /* etc. */ }

вместо

if(myPointer == NULL) { /* etc. */ }

Обоснование заключается в том, что первая версия защитит кодер от опечаток кода, например, заменив "==" на "=" (потому что запрещено назначать значение постоянному значению).

Далее можно считать продолжение этого ограниченного шаблона if(NULL == p):

Почему константные параметры могут быть полезны для кодера

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

Например, такой код будет отображаться, когда компилятор может мне помочь:

void bar_const(const int & param) ;
void bar_non_const(int & param) ;

void foo(const int param)
{
   const int value = getValue() ;

   if(param == 25) { /* Etc. */ } // Ok
   if(value == 25) { /* Etc. */ } // Ok

   if(param = 25) { /* Etc. */ } // COMPILE ERROR
   if(value = 25) { /* Etc. */ } // COMPILE ERROR

   bar_const(param) ;  // Ok
   bar_const(value) ;  // Ok

   bar_non_const(param) ;  // COMPILE ERROR
   bar_non_const(value) ;  // COMPILE ERROR

   // Here, I expect to continue to use "param" and "value" with
   // their original values, so having some random code or error
   // change it would be a runtime error...
}

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

Почему это не важно для пользователя

Бывает так:

void foo(const int param) ;

и

void foo(int param) ;

имеют одну и ту же подпись.

Это хорошо, потому что, если разработчик функции решает, что параметр считается const внутри функции, пользователь не должен этого и не должен знать.

Это объясняет, почему мои объявления функций для пользователей опускают константу:

void bar(int param, const char * p) ;

чтобы сделать декларацию максимально ясной, в то время как мое определение функции добавляет ее как можно больше:

void bar(const int param, const char * const p)
{
   // etc.
}

чтобы сделать мой код максимально надежным.

Почему в реальном мире он мог сломать

Я был укушен моим рисунком.

На некотором сломанном компиляторе, который останется анонимным (имя которого начинается с " Sol" и заканчивается на " aris CC" ), две приведенные выше сигнатуры можно рассматривать как (в зависимости от контекста), и, таким образом, ссылка на runtime будет возможно сбой.

Поскольку проект был скомпилирован на платформах Unix (Linux и Solaris), на этих платформах символы undefined оставались разрешенными при выполнении, что вызвало ошибку времени выполнения в середине выполнения процесса.

Итак, поскольку мне пришлось поддерживать указанный компилятор, я закончил загрязнение даже моих заголовков прототипами с констеблями.

Но я все же все же считаю этот шаблон добавления const в определении функции хорошим.

Примечание. У Sun Microsystems даже есть шары, чтобы скрыть свое сломанное рукотворство, так как " это злой шаблон, так что вы не должны использовать его". см. http://docs.oracle.com/cd/E19059-01/stud.9/817-6698/Ch1.Intro.html#71468

Последнее примечание

Следует отметить, что Бьярне Страуструп, похоже, возражал против рассмотрения void foo(int) того же прототипа, что и void foo(const int):

Не все признанные функции, на мой взгляд, являются улучшением. Например, [...] правило, что void f (T) и void f (const T) обозначает ту же функцию (предложенную Томом Plum для совместимости с C) [имеет] сомнительное различие в том, что он был проголосован на С++ "над моим мертвым телом".

Источник: Бьярне Страуструп
Развитие языка в и для реального мира: С++ 1991-2006, 5. Особенности языка: 1991-1998, p21.
http://www.stroustrup.com/hopl-almost-final.pdf

Это забавно считать, что Херб Саттер предлагает противоположную точку зрения:

Указание: Избегайте параметров const-by-value const в объявлениях функций. Все равно сделайте параметр const в том же определении функции, если он не будет изменен.

Источник: Herb Sutter
Исключительный С++, Пункт 43: Const-Correctness, p177-178.

Ответ 2

Это обсуждалось много раз, и в основном люди в конечном итоге должны согласиться не согласиться. Лично я согласен с тем, что это бессмысленно, и стандарт неявно соглашается - определитель уровня (или volatile) верхнего уровня не является частью сигнатуры функции. На мой взгляд, желание использовать квалификатор верхнего уровня, подобный этому, указывает (сильно), что человек может заплатить услугу lip за отделение интерфейса от реализации, но на самом деле не понимает различия.

Еще одна небольшая деталь: она применима к ссылкам так же, как и указатели, хотя...

Ответ 3

Это делает компилятор частью работы по поиску ваших ошибок. Если вы не должны изменять его, сделайте его const, и если вы забудете, компилятор будет кричать на вас.

Ответ 4

Если bar отмечен const, как указано выше, тогда человек, читающий код, зная, что передано, знает все время точно, что содержит bar. Нет необходимости смотреть на какой-либо код заранее, чтобы увидеть, изменился ли бар в любой момент на этом пути. Это упрощает рассуждение о коде и, таким образом, уменьшает вероятность ошибок в ошибках.

Я сам проголосую за "хорошую практику". Конечно, в наши дни я тоже в значительной степени превращаюсь в функциональные языки, поэтому...


Упомянув комментарий ниже, рассмотрите этот исходный файл:

// test.c++

bool testSomething()
{
    return true;
}

int test1(int a)
{
    if (testSomething())
    {
        a += 5;
    }
    return a;
}

int test2(const int a)
{
    if (testSomething())
    {
        a += 5;
    }
    return a;
}

В test1 мне не известно, что возвращаемое значение будет без чтения (потенциально значимого и/или запутанного) тела функции и без отслеживания ( потенциально отдаленный, значительный, запутанный и/или источник-недоступный) функции testSomething. Кроме того, изменение a может быть результатом ужасающей опечатки.

Эта же опечатка в test2 приводит к этому во время компиляции:

$ g++ test.c++
test.c++: In function ‘int test2(int)’:
test.c++:21: error: assignment of read-only parameter ‘a’

Если это была опечатка, она была поймана за меня. Если это не опечатка, лучше выбрать кодировку, IMO:

int test2(const int a)
{
    int b = a;
    if (testSomething())
    {
        b += 5;
    }
    return b;
}

Даже полупеченный оптимизатор будет генерировать идентичный код, как в случае test1, но вы сигнализируете о том, что нужно уделить внимание и внимание.

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

Ответ 5

Я склонен быть немного дьяволом const, поэтому мне это нравится. В основном полезно указать читателю кода, что переменная, переданная в wont быть изменена; таким же образом, что я пытаюсь отметить любую другую переменную, которую я создаю в теле функции как const, если она не была изменена.

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