В 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;
Другие вопросы по тегам