В C++ допустимо ли использование const после идентификатора типа?
Моему коллеге 0 на 2 по вопросам, которые он вдохновил ( 1, 2), поэтому я подумал, что дам ему шанс наверстать упущенное.
Наше последнее разногласие связано со стилем вопроса о том, где поместить "const" в декларации.
Он считает, что он должен идти либо перед типом, либо после указателя. Причина заключается в том, что это то, что обычно делают все остальные, и другие стили могут вводить в заблуждение. Таким образом, указатель на константу int и указатель на int будут соответственно:
const int *i;
int * const i;
Тем не менее, я все равно запутался. Мне нужны правила, которые являются последовательными и простыми для понимания, и единственный способ, которым я могу понять смысл "const", состоит в том, что он идет после того, что он модифицирует. Есть исключение, которое позволяет ему идти перед конечным типом, но это исключение, поэтому мне легче, если я его не использую.
Таким образом, указатель на константу int и указатель на int будут соответственно:
int const * i;
int * const i;
В качестве дополнительного преимущества, выполнение таких действий облегчает понимание более глубоких уровней косвенности. Например, указатель на постоянный указатель на int будет ясно:
int * const * i;
Я утверждаю, что если кто-то узнает это по-своему, у него не будет проблем с выяснением того, к чему это приводит.
В конечном итоге проблема заключается в том, что он считает, что помещать const после int настолько ужасно некрасиво и настолько вредно для читабельности, что его следует запретить в руководстве по стилю. Конечно, я думаю, что в любом случае руководство должно предложить сделать это по-своему, но в любом случае мы не должны запрещать один подход.
Редактировать: я получил много хороших ответов, но ни один из них не касается непосредственно моего последнего абзаца ("Основная проблема"). Многие люди настаивают на последовательности, но так ли это желательно в этом случае, что это хорошая идея, чтобы запретить другой способ сделать это, а не просто обескуражить его?
15 ответов
Самое главное это последовательность. Если для этого нет руководств по кодированию, выберите одно и придерживайтесь его. Но, если ваша команда уже имеет стандарт де-факто, не меняйте его!
Тем не менее, я думаю, что гораздо более распространенным является
const int* i;
int* const j;
потому что большинство людей пишут
const int n;
вместо
int const n;
Примечание: простой способ прочитать указатель const
Нужно прочитать декларацию, начинающуюся справа.
const int* i; // pointer to an int that is const
int* const j; // constant pointer to a (non-const) int
int const* aLessPopularWay; // pointer to a const int
Есть класс примеров, где размещение констант справа от типа также помогает избежать путаницы.
Если у вас есть тип указателя в typedef, то невозможно изменить константу типа to:
typedef int * PINT;
const PINT pi;
'pi' по-прежнему имеет тип 'int * const', и это независимо от того, где вы пишете 'const'.
Я надеюсь, что это объяснение из часто задаваемых вопросов Б. Страуструпа по стилю и технике даст вам определенный ответ.
Bjarne Stroustrup: часто задаваемые вопросы по стилю и технике C++
Я лично предпочитаю:
int const* pi;
int* const pi;
Так как const
идентифицирует левый токен, который должен быть постоянным
И вы определенно сохраняете постоянство при использовании чего-то такого:
int const* const pi;
Вместо того, чтобы писать непоследовательно:
const int* const pi;
А что будет, если у вас есть указатель на указатель и так далее:
int const* const* const pi;
Вместо:
const int* const* const pi;
Я был на конференции, где Бьярн Страуструп давал презентацию, и он использовал что-то вроде const int* i; Кто-то спросил его, почему этот стиль, и он ответил (перефразируя): "людям нравится сначала видеть const, когда что-то постоянно".
Люди обычно используют const int* blah
потому что он хорошо читает по-английски. Я бы не стал недооценивать полезность этого.
Я считаю, что int* const blah
вариации достаточно редки, поэтому обычно нецелесообразно делать более общее определение в обратном направлении. В общем, я не фанат чего-либо, что даже слегка затеняет код в общем случае, хотя в исключительном случае это может дать некоторую номинальную выгоду.
Смотрите также "if (1 == a)". Некоторым людям действительно нравится писать код, который не читается как английский. Я не один из тех людей.
На самом деле, правила const просты. Посмотрите налево, затем направо. Настолько просто, что я не думаю, что в руководстве по стилю это заслуживает особого внимания.
Если бы это были только переменные и указатели на них, const
или нет, это не будет иметь большого значения. Но учтите:
class MyClass
{
public:
int foo() const;
};
Ни за что const
мог быть написан где-нибудь еще, но завершая функцию, к которой он относится.
Чем короче руководство по стилю, тем более вероятно, что разработчики будут следовать ему. И самое короткое правило, и единственное правило, которое даст вам последовательность, это:
const
Ключевое слово всегда следует за тем, на что оно ссылается.
Итак, я бы сказал 0:3 для вашего коллеги здесь.
Относительно вашей "главной проблемы": ради руководства по стилю не имеет значения, "препятствует" руководство или запрещает "то, против чего оно выступает". Это социальная проблема, политика. Само руководство по стилю должно быть как можно более четким и коротким. Длинные руководства по стилю просто игнорируются всеми (кроме руководства по поиску виновных), поэтому просто пишите "делай" или "не делай" и заявляй, что ты делаешь с нарушениями руководства в другом месте (например, в политике компании). о том, как проводятся экспертные оценки).
Пока нет существенной разницы между const int
а также int const
(и я видел оба стиля в использовании), есть разница между const int *
а также int * const
,
Во-первых, у вас есть указатель на const int. Вы можете изменить указатель, но вы не можете изменить значение, на которое он указывает. Во втором у вас есть константный указатель на int. Вы не можете изменить указатель (надеюсь, он будет инициализирован по вашему вкусу), но вы можете изменить значение указателя на int.
Правильное сравнение с const int *
а также int const *
, которые оба являются указателями на const int.
Помните, что *
не обязательно работает, как вы могли бы. Декларация int x, y;
будет работать, как вы ожидаете, но int* x, y;
объявляет один указатель на int и один int.
Помещение "const" после объявления типа имеет гораздо больше смысла, если вы научитесь читать объявления типа C++ справа налево.
Я собираюсь привязать твою корову на 0-к-3:)
В конечном итоге проблема заключается в том, что он считает, что помещать const после int настолько ужасно некрасиво и настолько вредно для читабельности, что его следует запретить в руководстве по стилю.
В самом деле?
Покажите мне программиста, который увязает, когда видит:
int foo() {
}
против
int foo()
{
}
... и я покажу вам программиста, который не уделяет достаточно внимания деталям.
Ни один профессиональный программист, достойный его соли, не будет иметь проблем с поверхностными различиями в стиле.
РЕДАКТИРОВАТЬ: Это правда, что const int * и int * const не означают совершенно одно и то же, но это не главное. Сотрудник OP отметил, что различия в стиле делают код трудным для понимания и сопровождения. С этим утверждением я не согласен.
Позвольте мне поставить 2¢ в обсуждение.
В современном C++ я бы рекомендовал придерживаться int const
вместо const int
подчеркнуть разницу между const
а также constexpr
, Это поможет программисту запомнить это const int
не всегда можно считать постоянной времени компиляции, потому что:
- компилятор может (будет?) выделять для него память,
- эта память может быть даже не защищена от записи (сегмент стека),
- такая константа не может выполнять все обязанности
constexpr
может
Лично мне (и это личное предпочтение) я нашел чтение типов объявлений справа налево проще всего. Особенно, когда вы начинаете бросать ссылки в микс.
std::string const& name = plop; // reference to const string.
const std::string& flame =plop; // reference to string const;
// That works better if you are German I suppose :-)
Правила хороши для подражания. Чем проще правила, тем лучше. Const идет справа от того, что const.
Возьмите эту декларацию:
int main (int const argc, char const * const * const argv)...
Я согласен с вами обоими. Вы должны поставить const после типа. Я также нахожу, глядя на это мерзость, которая должна быть уничтожена. Но мой недавний поиск чудесных параметров const value позволил мне понять, почему ставить const на второй имеет смысл.
int *
int const *
int * const
int const * const
Просто глядя на это, волосы на моей шее стоят. Я уверен, что это смутило бы моих коллег.
РЕДАКТИРОВАТЬ: Мне просто интересно об использовании этого в классах:
class Foo {
Bar* const bar;
Foo(const Foo&) = delete; // would cause too many headaches
public:
Foo() : bar(new Bar) {}
~Foo() { delete bar; }
};
bar
в этом примере функционально эквивалентно Bar&
но это в куче и может быть удалено. В течение срока действия каждого Foo, с ним будет связан один Бар.
Мне нравится использовать следующую форму для объявления "констант манифеста". В этом случае само значение является константой, поэтому я ставлю сначала "const" (то же самое, что и Бьярне), чтобы подчеркнуть, что константность должна проявляться во время компиляции и использоваться компилятором как таковая для конкретных оптимизаций.
const int i = 123;
Для объявления ссылок, которые не будут использоваться для изменения значения, я использую следующую форму, которая подчеркивает тот факт, что идентификатор является "постоянной ссылкой". Указанное значение может быть или не быть константой. [Связанное обсуждение: Вы бы даже использовали "const" для параметров функции, если бы они не были указателями или ссылками? Использование 'const' для параметров функции ]
void fn( int const & i );
Для указателей я использую ту же форму, что и для ссылок, по сути по той же причине (хотя термин "постоянный указатель" кажется немного более двусмысленным, чем "постоянный указатель").
void fn( int const * i );
Кроме того, как заметил другой автор, эта форма остается последовательной, если у вас есть несколько уровней косвенности.
void fn( int const * const * i );
Последний сценарий, где вы объявляете указатель, который является постоянным, довольно редок в моем опыте с C++. В любом случае, у вас действительно нет выбора здесь. [Этот случай демонстрирует, что наиболее последовательный подход заключался бы в том, чтобы поставить слово "const" после типа - поскольку это фактически требуется для этого конкретного объявления.]
void fn( int * const i );
... если вы не используете typedef.
typedef int * IntPtr;
void fn1( const IntPtr i );
void fn2( IntPtr const i );
Последнее замечание: если вы не работаете в домене низкого уровня, большая часть кода C++ никогда не должна объявлять указатель. Следовательно, этот аспект этого обсуждения, вероятно, больше относится к C.
В современном C++11 вы также можете использовать шаблон typedefs, чтобы внести ясность в сложные объявления:
template<typename T>
using Const = const T;
template<typename T>
using Ptr = T*;
Три заявления, которые вы упомянули в своем вопросе:
int const * a;
int * const b;
int * const * c;
будет тогда написано как:
Ptr<Const<int>> a;
Const<Ptr<int>> b;
Ptr<Const<Ptr<int>>> c;