Корректность корректности для значений параметров

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

// header
int func(int i);

// cpp
int func(const int i) {
    return i;
}

Является ли это действительно лучшей практикой? Потому что я никогда не видел, чтобы кто-то это делал. Я видел эту цитату (не уверен в источнике) в других местах, о которой это говорилось:

"На самом деле, для компилятора, сигнатура функции одинакова, включаете ли вы эту константу перед параметром значения или нет".

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

Во втором абзаце говорится, что он не помещает const в объявление. Я предполагаю, что это связано с тем, что константа параметра значения бессмысленна как часть определения интерфейса. Это деталь реализации.

Исходя из этой рекомендации, рекомендуется ли это также для указательных значений параметров указателя? (Это не имеет смысла для ссылочного параметра, поскольку вы не можете переназначить ссылку.)

// header
int func1(int* i);
int func2(int* i);

// cpp
int func1(int* i) {
    int x = 0;

    *i = 3; // compiles without error
    i = &x; // compiles without error

    return *i;
}
int func2(int* const i) {
    int x = 0;

    *i = 3; // compiles without error
    i = &x; // compile error

    return *i;
}

Резюме: Создание параметров значения полезно для обнаружения некоторых логических ошибок. Это лучшая практика? Вы доходите до крайности, оставляя const из файла заголовка? Это также полезно для значений указателя const? Почему или почему нет?

Некоторые ссылки:

ключевое слово С++ const - используйте либерально? Использование 'const' для параметров функции

Пример того, когда значения параметра const полезны:

bool are_ints_equal(const int i, const int j) {
    if (i = j) { // without the consts this would compile without error
        return true;
    } else {
        return false;
    }
    // return i = j; // I know it can be shortened
}

Ответы

Ответ 1

Мое занятие:

Это не плохая идея, но проблема незначительна, и ваша энергия может быть лучше потрачена на другие вещи.

В вашем вопросе вы представили хороший пример того, когда он может поймать ошибку, но иногда вы также делаете что-то вроде этого:

void foo(const int count ...)
{
   int temp = count;  // can't modify count, so we need a copy of it
   ++temp;

   /* ... */
}

Плюсы и минусы являются незначительными в любом случае.

Ответ 2

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

Тем не менее, мне иногда кажется полезным для меня, как проверка того, что моя реализация не делает то, что я не намерен (как в примере в конце вашего вопроса).

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

Например, я могу реализовать функцию C, которая берет пару указателей на буфер - указатель на начало и указатель на конец. Я собираюсь помещать данные в буфер, но хочу убедиться, что я не переполняю конец. Поэтому внутри функции есть код, который будет увеличивать указатель, когда я добавляю к нему данные. Создание указателя в конце буфера a const параметр гарантирует, что я не буду кодировать ошибку, которая случайно увеличивает указатель конечной границы вместо указателя, который я действительно должен увеличивать.

Итак, функция fillArray с такой сигнатурой:

size_t fillArray( data_t* pStart, data_t* const pEnd);

предотвратит случайное увеличение pEnd, когда я действительно хочу увеличить pStart. Это не огромная вещь, но я уверен, что все, кто запрограммировал на какое-то время в C, столкнулись с такой ошибкой.

Ответ 3

К сожалению, некоторые компиляторы (я смотрю на вас, Sun CC!) неправильно различают аргументы, объявленные как const, и не объявленные так, и вы можете получить ошибки о функциях undefined.

Ответ 4

Я думаю, что это зависит от вашего личного стиля.

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

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

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

Ответ 5

Если присутствует ключевое слово const; это означает, что значение "i" (которое является типом const) не может быть изменено. Если значение "i" изменяется внутри функции foo, компилятор будет вызывать ошибку: "

Невозможно изменить объект const

Но изменение '* i' (т.е. * я = 3;) означает, что вы не изменяете значение "i", а значение адреса, указанное "i"

Собственно, функция const подходит для больших объектов, которые не должны быть изменены функцией.

Ответ 6

Мы все время должны время от времени распутывать код другого С++. И что кто-то еще С++-код является полным беспорядком по определению: D.

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

Итак, я очень ценю, когда кто-то другой имеет const везде (включая значения параметров): D

Ответ 7

Мне нравится константная корректность для таких ситуаций:
void foo(const Bar &b) //I know b cannot be changed
{
//do something with b
}

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