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

Лучше ли удалять "const" перед "примитивными" типами, которые используются в качестве параметров функции в заголовке?

В процессе проверки кода один из моих коллег упомянул мне, что "const" перед "примитивными типами", используемыми в качестве параметра функции в заголовке, не имеет смысла, и он рекомендовал удалить эти "константы". В таких случаях он предложил использовать "const" только в исходном файле. Примитивные типы означают такие типы, как "int", "char", "float" и т.д.

Ниже приведен пример.

example.h

int ProcessScore(const int score);

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}

Его предложение делает следующее:

example.h

int ProcessScore(int score);  // const is removed here.

example.cc

int ProcessScore(const int score) {
  // Do some calculation using score
  return some_value;
}

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

Может ли кто-нибудь дать совет по этому поводу?

4b9b3361

Ответ 1

Для всех типов (а не только примитивов) в объявлении функции игнорируются квалификаторы верхнего уровня. Итак, следующие четыре все объявляют одну и ту же функцию:

void foo(int const i, int const j);
void foo(int i, int const j);
void foo(int const i, int j);
void foo(int i, int j);

Однако спецификатор const не игнорируется внутри тела функции. Там это может повлиять на константную корректность. Но это детализация функции. Таким образом, общий консенсус таков:

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

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

Ответ 2

Параметр функции, объявленный как const, и const, остается неизменным при достижении разрешения перегрузки. Так, например, функции

void f(int);
void f(const int);

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

Вот точная цитата из стандарта.

После создания списка типов параметров, любые cv-квалификаторы верхнего уровня, изменяющие тип параметра, удаляются при формировании тип функции. Полученный список преобразованных типов параметров и наличие или отсутствие многоточия или пакет параметров функции - это список параметров-типа. [Примечание: это преобразование не влияют на типы параметров. Например, int(*)(const int p, decltype(p)*) и int(*)(int, const int*) являются идентичными типами. - конечная нота]

Полезность константы в определении функции является дискуссионной: рассуждение за ней совпадает с с использованием const для объявления локальной переменной - она ​​демонстрирует другим программистам, читающим код, это значение не будет изменено внутри функции.

Ответ 3

Следуйте рекомендациям, приведенным в обзоре кода.

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

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

int a = 7;
do_something( a );

void do_something(       int& x );  // 'a' may be modified
void do_something( const int& x );  // I will not modify 'a'
void do_something(       int  x );  // no one cares what happens to x

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

edit2: Мне также очень нравится информация в Ответы StoryTellers

Ответ 4

Как и многие другие люди, с точки зрения API, все эквивалентны и равны для разрешения перегрузки:

void foo( int );
void foo( const int );

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

Без каких-либо четко определенных правил кодирования разработчика, которые прямо определяют это, скалярные аргументы const не имеют очевидного смыслового значения.

От потребителя: const int не изменяет ваш вход. Он все равно может быть буквальным или может быть из другой переменной (как const, так и не const)

От разработчика: const int накладывает ограничение на локальную копию переменной (в данном случае аргумент функции). Это просто означает изменить аргумент, вы берете другую копию переменной и изменяете ее.

При вызове функции, принимающей значение аргумента, создается копия этого аргумента в стеке для вызываемой функции. Это дает функции локальную копию аргумента для всей ее области, которая затем может быть изменена, использована для вычислений и т.д. - без влияния на исходный вход, переданный в вызов. Фактически, это обеспечивает локальный переменный аргумент его ввода.

Отметив аргумент как const, это просто означает, что эта копия не может быть изменена; но он не запрещает разработчику копировать его и вносить изменения в эту копию. Так как это была копия с самого начала, она не обеспечивает все это изнутри реализации - и в конечном итоге не имеет большого значения с точки зрения потребителя.

Это противоречит передаче по ссылке, где ссылка на int& семантически отличается от const int&. Первый способен мутировать свой вклад; последний может только наблюдать за входом (при условии, что реализация не const_cast const -ness), но позволяет игнорировать эту возможность); таким образом, const -ness на ссылках имеет подразумеваемое семантическое значение.

Это не дает большой пользы публичному API; и (imo) вводит ненужные ограничения в реализацию. Как произвольный, надуманный пример - простая функция типа:

void do_n_times( int n )
{
   while( n-- > 0 ) {
       // do something n times
   } 
}

теперь нужно будет записать с помощью ненужной копии:

void do_n_times( const int n )
{
    auto n_copy = n;
    while( n_copy-- > 0 ) {
        // do something n times
    }
}

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

TL; DR: const скалярные типы в общедоступном API не передают семантический смысл, если явно не определены вашими собственными рекомендациями для вашего домена.