Использование 'const' для параметров функции

Как далеко вы зашли с const? Вы просто делаете функции const, когда это необходимо, или вы делаете все возможное и используете его везде? Например, представьте простой мутатор, который принимает единственный логический параметр:

void SetValue(const bool b) { my_val_ = b; }

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

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

.h файл

void func(int n, long l);

.cpp файл

void func(const int n, const long l)

Есть причина для этого? Мне это кажется немного необычным.

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

selwyn 26.09.2008 00:31

Я согласен. :-) (С вопросом, а не последним комментарием!) Если значение не должно изменяться в теле функции, это может помочь остановить глупые ошибки == или =, вы никогда не должны помещать const в оба, (если он передается по значению, иначе вы должны) Это недостаточно серьезно, чтобы спорить об этом!

Chris Huang-Leaver 20.04.2009 18:24

@selwyn: Даже если вы передадите в функцию const int, она будет скопирована (поскольку это не ссылка), поэтому константа не имеет значения.

jalf 05.05.2009 03:20

Те же дебаты происходят и в этом вопросе: stackoverflow.com/questions/1554750/…

Partial 12.11.2009 18:34

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

user898058 20.09.2011 00:45

Теперь с семантикой перемещения, имеющей const, может снизиться производительность

Jimmy R.T. 18.12.2020 17:41
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
437
6
283 188
30
Перейти к ответу Данный вопрос помечен как решенный

Ответы 30

Ответ принят как подходящий

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

Лично я не использую const, за исключением параметров ссылки и указателя. Для скопированных объектов это не имеет значения, хотя может быть безопаснее, поскольку сигнализирует о намерении внутри функции. Это действительно призыв к суждению. Я обычно использую const_iterator, когда зацикливаюсь на чем-то, и я не собираюсь его изменять, поэтому я предполагаю, что каждому свое, если строго соблюдается правильность const для ссылочных типов.

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

Michał Górny 09.01.2010 14:53

«Лично я не использую const, за исключением параметров ссылки и указателя». Может быть, вам следует пояснить, что «Я стараюсь не использовать лишние квалификаторы в объявлениях функций, а использую const там, где это имеет полезное значение».

Deduplicator 09.09.2014 20:53

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

Ultimater 01.08.2016 23:27

int getDouble(int a){ ++a; return 2*a; } Попробуйте это. Конечно, ++a здесь не при чем, но может можно найти там в длинной функции, написанной более чем одним программистом в течение длительного периода времени. Я настоятельно рекомендую написать int getDouble( const int a ){ //... }, который будет генерировать ошибку компиляции при обнаружении ++a;.

dom_beau 27.03.2017 06:58

Все зависит от того, кому какая информация нужна. Вы предоставляете параметр по стоимости, чтобы вызывающий не нужно ничего знать определял, что вы делаете (внутренне) с ним. Так что напишите class Foo { int multiply(int a, int b) const; } в шапке. В вашей реализации ты заботишься вы можете пообещать не изменять a и b, поэтому int Foo::multiply(const int a, const int b) const { } имеет здесь смысл. (Примечание: и вызывающая, и реализация заботятся о том, чтобы функция не изменяла свой объект Foo, следовательно, константу в конце своего объявления)

CharonX 01.10.2018 12:54

Я также не думаю, что этот «ответ» справедлив, если я считаю, что это плохой стиль, что это не имеет особого значения. В вызове суждения есть некоторая практическая истина, но все аргументы для const в общем случае переменной применимы, и это проблема / недосмотр языка, которая не рассматривается по-разному для объявления. Я бы сказал, что меня не устраивают объяснения «плохой стороны», которые я вижу в других местах (которые имеют другие побочные эффекты). Для stackoverflow было плохо выбрать это в качестве ответа, не потому, что он не может быть частью более крупного ответа, а потому, что он неполон для сложной проблемы C++.

Jason Newton 05.06.2019 08:11

@ MichałGórny просто представьте первую строку вашей функции как void func (unsigned char const * const buffer, size_t const length); это выглядит неуклюже. Но вместо этого, если мы напишем как void func (unsigned char const * buffer, size_t length); , это выглядит намного лучше

Abhishek Garg 26.11.2019 14:11

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

Zoë Sparks 28.10.2020 12:55

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

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

Сигнатура функции для

void foo(int a);

и

void foo(const int a);

то же самое, что объясняет ваши .c и .h

Асаф

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

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

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

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

gbjbaanb 23.09.2008 00:19

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

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

Я был бы более склонен опустить ключевое слово const в файле cpp, чем в заголовке, но, поскольку я стараюсь вырезать + вставить их, они будут храниться в обоих местах. Я понятия не имею, почему компилятор позволяет это, я думаю, что это компилятор. Лучше всего поместить ключевое слово const в оба файла.

Я вообще этого не понимаю. Почему Не могли бы вы опустить его в файле cpp (определение функции)? Вот где это на самом деле что-то значит и может обнаруживать ошибки. Почему как вы думаете, лучше всего использовать const в обоих местах? В заголовочном файле (объявлении функции) это ничего не значит и загромождает API. Может быть, есть небольшая ценность в том, чтобы объявления decl и defn выглядели одинаково, но мне кажется, что это действительно незначительное преимущество по сравнению с проблемой загромождения API.

Don Hatch 14.10.2016 00:55

@DonHatch 8 лет спустя, вау. В любом случае, как сказал OP: «Я также был удивлен, узнав, что вы можете опустить const из параметров в объявлении функции, но можете включить его в определение функции».

gbjbaanb 14.10.2016 15:53

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

Все константы в ваших примерах не имеют цели. C++ по умолчанию является передачей по значению, поэтому функция получает копии этих целых чисел и логических значений. Даже если функция изменяет их, копия вызывающего не затрагивается.

Поэтому я бы избегал лишних констант, потому что

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

На самом деле нет причин делать параметр значения «константой», поскольку функция в любом случае может изменять только копию переменной.

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

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

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

Что действительно важно, так это помечать методы как константы, если они не изменяют свой экземпляр. Делайте это на ходу, потому что в противном случае вы можете получить либо большое количество const_cast <>, либо вы можете обнаружить, что маркировка метода const требует изменения большого количества кода, потому что он вызывает другие методы, которые должны были быть помечены как const.

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

Параметр Const полезен только тогда, когда параметр передается по ссылке, то есть по ссылке или указателю. Когда компилятор видит параметр const, он проверяет, что переменная, используемая в параметре, не изменяется в теле функции. Зачем кому-то нужно делать параметр по значению постоянным? :-)

По многим причинам. Создание параметра по значению const четко указывает: «Мне не нужно изменять это, поэтому я заявляю об этом. Если я попытаюсь изменить его позже, выдаст мне ошибку времени компиляции, чтобы я мог исправить свою ошибку или снять отметку const ». Так что это вопрос как гигиены кода, так и безопасности. Несмотря на все, что требуется для добавления в файлы реализации, люди должны делать это как чистый рефлекс, ИМО.

underscore_d 23.09.2018 20:02

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

Я не знал о разнице в объявлении файлов .h / .cpp, но в этом есть смысл. На уровне машинного кода ничего не является «константой», поэтому, если вы объявляете функцию (в .h) как неконстантную, код будет таким же, как если бы вы объявили ее как константу (не считая оптимизаций). Однако это поможет вам привлечь компилятор, чтобы вы не изменили значение переменной внутри реализации функции (.ccp). Это может пригодиться в случае, если вы наследуете интерфейс, который позволяет изменять, но вам не нужно изменять параметр для достижения требуемой функциональности.

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

Насколько я знаю, компилятор вполне может увидеть параметр значения const и сказать: «Эй, эта функция все равно не изменяет его, поэтому я могу передать его по ссылке и сохранить несколько тактов». Я не думаю, что он когда-либо мог бы сделать такое, поскольку он меняет сигнатуру функции, но в этом есть смысл. Может быть, он выполняет какие-то другие манипуляции со стеком или что-то в этом роде ... Дело в том, что я не знаю, но я знаю, что попытки быть умнее компилятора только приводят меня к стыду.

У C++ есть дополнительный багаж, связанный с идеей константной корректности, поэтому он становится еще более важным.

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

underscore_d 23.09.2018 20:07

Следующие две строки функционально эквивалентны:

int foo (int a);
int foo (const int a);

Очевидно, вы не сможете модифицировать a в теле foo, если он определен вторым способом, но снаружи нет никакой разницы.

const действительно пригодится с параметрами ссылки или указателя:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Это говорит о том, что foo может принимать большой параметр, возможно, структуру данных размером в гигабайты, без ее копирования. Кроме того, он говорит вызывающему: «Foo не будет * изменять содержимое этого параметра». Передача константной ссылки также позволяет компилятору принимать определенные решения о производительности.

*: Если он не откажется от константности, но это другой пост.

Вопрос не в этом; конечно, для аргументов, на которые есть ссылка или указывается, рекомендуется использовать const (если указанное или указанное значение не изменяется). Обратите внимание, что в вашем примере указателя константой является не параметр; это то, на что указывает параметр.

tml 05.05.2016 12:12

> Передача константной ссылки также позволяет компилятору принимать определенные решения о производительности. классическая ошибка - компилятор должен сам определять константу, ключевое слово const в этом не помогает благодаря псевдониму указателя и const_cast

jheriko 04.10.2016 19:54

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

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

Наконец, функция, которая не изменяет текущий объект (this), может и, вероятно, должна быть объявлена ​​как const. Пример ниже:

int SomeClass::GetValue() const {return m_internalValue;}

Это обещание не изменять объект, к которому применяется этот вызов. Другими словами, вы можете вызвать:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Если бы функция не была константной, это привело бы к предупреждению компилятора.

const is pointless when the argument is passed by value since you will not be modifying the caller's object.

Неправильный.

Речь идет о самодокументировании вашего кода и ваших предположений.

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

Кроме того, как кто-то упоминал ранее, мог бы помогает компилятору немного оптимизировать работу (хотя это далеко не всегда).

Полностью согласен. Все дело в общении с людьми и ограничении того, что можно сделать с переменной, тем, что должно быть сделано.

Len Holgate 23.09.2008 01:08

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

tonylo 23.09.2008 12:02

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

Richard Corden 23.09.2008 12:20

@tonylo: вы неправильно поняли. Речь идет о том, чтобы пометить локальную переменную как константу внутри блока кода (который является функцией). Я бы сделал то же самое для любой локальной переменной. Он ортогонален константно-корректному API, что действительно также важно.

rlerallut 23.09.2008 12:50

Не совсем, я думаю, вы просто переоцениваете полезность const. Я думаю, [полезность] появляется тогда, когда важно определить право собственности на объект. Взгляните на API POSIX, откройте (2), например, путь (указатель) стоит сделать константой, для флагов и режимов это не имеет значения.

tonylo 24.09.2008 03:17

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

Adrian 01.10.2008 05:03

@tonylo, я к этому вопросу отношусь нейтрально. Если люди вводят в параметры const, хорошо. Но я не делаю этого в своем коде. Но упоминание руководств open и т. д. На самом деле не является аргументом. Потому что const не будет частью интерфейса. Так что показывать const на страницах руководства было бы неправильно.

Johannes Schaub - litb 13.10.2009 13:07

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

Johannes Schaub - litb 18.11.2009 01:28

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

Martin Beckett 15.09.2010 08:45

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

Giorgio 12.09.2011 16:03

«Reductio ad absurdum» - ответ призывает использовать «const» всякий раз, когда это возможно. Это просто приводит к загромождению API, которые труднее читать, и ограничивает алгоритмы. void bytecopy(char * CONST dest, const char * CONST source, CONST int count) { while(count--) { *dest++=*source++; } } не будет работать, и три из четырех констант (заглавные) не нужны.

Adisak 14.06.2012 19:51

«Вы всегда должны предполагать, что ваши коллеги - психопаты, пытающиеся достать вас любым возможным способом», - заставили меня громко смеяться на работе. +1 за это.

Cody Piersall 26.06.2013 18:15

Если ваши коллеги - психопаты, ожидайте, что они напишут struct Foo { void bar(const string); }; void Foo::bar(string s) { s = "I modified a string which was supposed to be const"; } «Так много для самодокументирования».

Oktalist 09.02.2014 03:51

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

LearnCocos2D 25.07.2014 10:20

Для меня есть еще одна причина написать здесь const. Я делаю это по веским причинам. Я привык делать параметры по умолчанию константными, так зачем останавливаться на типах POD? (только мое личное мнение)

Milania 27.05.2015 21:57

Я думаю, что использование const с параметрами, не являющимися ссылками / указателями, совпадает с окружающим оператором return с круглыми скобками.

mike 10.09.2016 21:51

Искренне согласен! Настоящий разработчик API использует const.

sailfish009 05.02.2018 03:10

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

DavidJ 28.06.2018 21:38

Позвольте мне не согласиться с вашим ответом здесь @rlerallut (хотя ему сейчас 10 лет). Константа в заголовке для аргументов по значению не дает значения или даже отрицательного значения. С одной стороны, это не выполняется компилятором, поэтому разработчику ничто не мешает удалить «const» в реализации (а затем забыть обновить заголовок). С другой стороны, пользователю заголовка не выгодно знать, что функция (предположительно - см. Ранее) использует значение константным способом. В лучшем случае это шум в заголовке, в худшем - обещания, которые исполнитель может легко нарушить.

CharonX 17.12.2018 18:21

Если бы только const был параметром по умолчанию, и вам пришлось бы изо всех сил, чтобы сделать параметр изменяемым, добавив к нему префикс ключевого слова. Существующий синтаксис C++ кажется похожим на то, как в TypeScript вам нужно написать дополнительный синтаксис для использования строгой типизации!

Sphynx 05.01.2019 01:02

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

Ах, я бы хотел, чтобы переменные были const по умолчанию, а изменчивый требовалось для неконстантных переменных :)

«Я бы хотел, чтобы переменные по умолчанию были константными» - оксюморон ?? 8-) Серьезно, как «конструирование» всего помогает распутать код? Если исходный автор изменил предположительно постоянный аргумент, как узнать, что переменная должна быть константой? Более того, подавляющее большинство (не аргументов) переменных предназначены быть ... переменными. Итак, компилятор должен сломаться очень скоро после того, как вы начали процесс, не так ли?

ysap 23.01.2017 10:59

@ysap, 1. Максимальное обозначение const позволяет мне видеть, какие части движутся, а какие нет. По моему опыту, многие местные жители де-факто постоянны, а не наоборот. 2. «Постоянная переменная» / «Неизменяемая переменная» может звучать как оксюморон, но это стандартная практика в функциональных языках, а также в некоторых нефункциональных языках; см. Rust, например: doc.rust-lang.org/book/variable-bindings.html

Constantin 29.01.2017 19:49

Также теперь стандартен в некоторых случаях в C++; например, лямбда [x](){return ++x;} - это ошибка; см. здесь

anatolyg 21.06.2017 17:08

Переменные по умолчанию "const" в Ржавчина :)

phoenix 31.07.2017 15:46

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

Sphynx 05.01.2019 01:14

@phoenix Для будущего читателя: Почему неизменяемость обязательна в Rust, если иное не указано в mut?

ynn 28.04.2020 00:37

Когда я зарабатывал на жизнь кодированием C++, я делал все, что мог. Использование const - отличный способ помочь компилятору вам в этом помочь. Например, создание возвращаемых значений вашего метода может спасти вас от таких опечаток, как:

foo() = 42

когда вы имели в виду:

foo() == 42

Если foo () определен для возврата неконстантной ссылки:

int& foo() { /* ... */ }

Компилятор с радостью позволит вам присвоить значение анонимному временному объекту, возвращаемому вызовом функции. Делаем это const:

const int& foo() { /* ... */ }

Устраняет эту возможность.

С каким компилятором это работало? GCC выдает ошибку при попытке скомпилировать foo() = 42: error: lvalue требуется как левый операнд присваивания

gavrie 09.02.2010 11:42

Это просто неверно. foo () = 42 то же самое, что 2 = 3, т.е. ошибка компилятора. А возвращать константу совершенно бессмысленно. Он ничего не делает для встроенного типа.

Josh 19.09.2011 08:14

Я столкнулся с таким использованием const и могу вам сказать, что в конечном итоге это приносит больше хлопот, чем пользы. Подсказка: const int foo() относится к другому типу, чем int foo(), что создает большие проблемы, если вы используете такие вещи, как указатели на функции, системы сигналов / слотов или boost :: bind.

Mephane 23.09.2011 15:32

Я исправил код, чтобы включить возвращаемое значение ссылки.

Avdi 29.11.2011 00:05

Разве const int& foo() не то же самое, что int foo(), из-за оптимизации возвращаемого значения?

Zantier 08.05.2014 15:20

Обозначение параметров значения «const» - вещь определенно субъективная.

Однако на самом деле я предпочитаю отмечать параметры значения как const, как в вашем примере.

void func(const int n, const long l) { /* ... */ }

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

Для короткой функции, возможно, будет пустой тратой времени / пространства иметь там 'const', поскольку обычно довольно очевидно, что аргументы не изменяются функцией.

Однако для более крупной функции это форма документации по реализации, и она обеспечивается компилятором.

Я могу быть уверен, что если я произведу какие-то вычисления с n и l, я смогу реорганизовать / переместить это вычисление, не опасаясь получить другой результат, потому что я пропустил место, где одно или оба были изменены.

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

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

const должно было быть значением по умолчанию в C++. Так :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

Совместимость с C слишком важна, по крайней мере для людей, которые разрабатывают C++, чтобы даже подумать об этом.

Roger Pate 16.03.2010 07:57

Интересно, я никогда об этом не думал.

Dan 19.09.2012 00:56

Точно так же unsigned должен был быть по умолчанию в C++. Как это: int i = 5; // i is unsigned и signed int i = 5; // i is signed.

hkBattousai 21.07.2015 16:49

Я говорю const ваши параметры значения.

Рассмотрим эту глючную функцию:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

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

другой способ - использовать if (0 == number) ... else ...;

Johannes Schaub - litb 14.11.2008 16:39

@ ChrisHuang-Leaver Ужасно, это не так, если вы говорите, как Йода, вы делаете: stackoverflow.com/a/2430307/210916

MPelletier 18.01.2012 00:04

GCC / Clang -Wall дает вам -Wparentheses, который требует, чтобы вы сделали это «if ((number = 0))», если это действительно то, что вы намеревались сделать. Что хорошо работает как замена Йоде.

Jetski S-type 25.10.2018 06:50

На эту тему есть хорошее обсуждение в старых статьях «Гуру недели» на comp.lang.C++. Moderated здесь.

Соответствующая статья GOTW доступна на веб-сайте Херба Саттера здесь.

Херб Саттер - действительно умный парень :-) Определенно стоит прочитать, и я согласен со ВСЕМИ его пунктами.

Adisak 14.06.2012 19:59

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

QBziZ 03.07.2012 16:31

По оптимизации компилятора: http://www.gotw.ca/gotw/081.htm

Лишние лишние константы - это плохо с точки зрения API:

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

Слишком много 'const' в API, когда оно не нужно, похоже на "воющий волк", в конечном итоге люди начнут игнорировать 'const', потому что он повсюду и большую часть времени ничего не значит.

Аргумент «reductio ad absurdum» для дополнительных констант в API хорош для этих первых двух пунктов: если подходят больше константных параметров, то каждый аргумент, который может содержать константу, ДОЛЖЕН иметь константу. Фактически, если бы это было действительно так хорошо, вы бы хотели, чтобы const была параметром по умолчанию и имела ключевое слово вроде «mutable» только тогда, когда вы хотите изменить параметр.

Итак, давайте попробуем ввести const, где только сможем:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Рассмотрим строку кода выше. Мало того, что объявление более загромождено, длиннее и сложнее для чтения, три из четырех ключевых слов const могут быть безопасно проигнорированы пользователем API. Однако дополнительное использование 'const' сделало вторую строку потенциально ОПАСНЫЙ!

Почему?

Быстрое неверное прочтение первого параметра char * const buffer может заставить вас подумать, что он не будет изменять память в буфере данных, который передается - однако это неверно! Излишняя const может привести к опасным и неверным предположениям о вашем API. при быстром сканировании или неправильном чтении.


Лишние константы тоже плохо с точки зрения реализации кода:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Если FLEXIBLE_IMPLEMENTATION неверно, то API «обещает» не реализовывать функцию первым способом, описанным ниже.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Это очень глупое обещание. Почему вы должны давать обещание, которое не приносит никакой пользы вашему вызывающему, а только ограничивает вашу реализацию?

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

Кроме того, это очень поверхностное обещание, которое легко (и юридически обойти).

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

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

Эти лишние константы стоят не больше, чем обещание плохого парня из фильма.


Но способность лгать становится еще хуже:

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

// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }

Однако верно и обратное ... вы можете поместить ложную константу только в объявление и игнорировать ее в определении. Это только делает лишнюю const в API более ужасной вещью и ужасной ложью - см. Этот пример:

struct foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

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

Взгляните на этот пример. Что более читабельно? Очевидно ли, что единственная причина использования дополнительной переменной во второй функции заключается в том, что какой-то дизайнер API добавил лишнюю константу?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

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

Почему отрицательные голоса? Будет гораздо полезнее, если вы оставите краткий комментарий к голосованию против.

Adisak 23.02.2013 03:26

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

touko 05.03.2013 00:49

Нет причин загромождать ваш API, чтобы раскрыть детали внутренней реализации. Достаточно просто, чтобы ваша реализация принимала параметры T parm_foo и сразу же назначала const &T foo=parm_foo, а затем использовала foo в функции - вы можете обеспечить постоянство параметров без ненужного беспорядка API для конечных пользователей.

Adisak 05.03.2013 04:39

@Adisak Я не вижу ничего плохого в вашем ответе как таковом, но из ваших комментариев кажется, что вы упускаете важный момент. Определение / реализация функции - это часть API нет, которая представляет собой только функцию декларация. Как вы уже сказали, объявление функций с параметрами const бессмысленно и добавляет беспорядка. Однако пользователям API может никогда не понадобиться его реализация. Тем временем разработчик может решить ограничить константой некоторые параметры в определении функции ТОЛЬКО для ясности, что совершенно нормально.

jw013 15.08.2013 23:17

@ jw013 Язык не допускает реализации / определения функций, которые отличаются от деклараций. Следовательно, они ИМЕЮТ должны быть одинаковыми. «Хороший» API должен открывать только то, что фактически составляет действительный «контракт» с пользователем, и не должен быть завален лишней чепухой, не влияющей на пользователя API. Я считаю, что хороший / чистый API лучше некоторых глупых поддельных ограничений реализации, которые легко обойти.

Adisak 20.08.2013 23:52

@Adisak Я не так хорошо знаком с педантичными деталями C++, как с C, но C99 определенно позволяет это (имея const в определении, но не в объявлении прототипа). Я никогда не соглашался с вами в том, что ясность API имеет первостепенное значение и не должна загромождаться деталями реализации.

jw013 21.08.2013 00:27

@ jw013 C++ позволяет создавать функции с одинаковыми именами и разными подписями. Это называется перегрузкой функций. Он реализуется компилятором, декорирующим фактическое имя C++ на основе параметров, которые применяются к функциям, не входящим в extern "C" {} (и, конечно, ко всем функциям-членам). В зависимости от используемых типов добавление ложной константы может фактически означать, что они считаются разными функциями и имеют разные декорированные имена, что может привести к ошибкам компоновщика.

Adisak 21.08.2013 04:44

@ jw013 правильный, void foo(int) и void foo(const int) - это одна и та же функция, а не перегрузки. ideone.com/npN4W4 ideone.com/tZav9R Здесь константа - это только деталь реализации тела функции и не влияет на разрешение перегрузки. Оставьте const в объявлении для более безопасного и аккуратного API, но поместите const в определение, если вы не собираетесь изменять скопированное значение.

Oktalist 09.02.2014 04:07

Интересный @Oktalist. Однако я не уверен, что мне очень нравится идея о том, что определение не соответствует объявлению, даже если это разрешено компилятором.

Adisak 13.02.2014 02:14

@Oktalist @ jw013 Хорошо, значит, вы можете не совпадать с const в объявлении и определении. Это делает излишнюю const только ужасной вещью и ужасной ложью, потому что вы можете игнорировать ее в своем определении - см. Этот пример: class foo { void test(int * const pi); }; void foo::test(int *pi) { pi++; }

Adisak 13.02.2014 21:29

@Adisak Я знаю, что это старый, но я считаю, что правильное использование общедоступного API было бы наоборот. Таким образом, разработчики, работающие над внутренними компонентами, не допускают ошибок, таких как pi++, когда они не должны этого делать.

RamblingMad 15.07.2014 08:28

@CoffeeandCode Я вижу случайное использование const в реализации функции, если вы решите ограничить себя от изменения переменной по значению в реализации, но никогда нет причин иметь ложную и ненужную константу в заголовке / общедоступном API определение. Это ложь, потому что реализация может это игнорировать.

Adisak 07.10.2014 04:28

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

Adisak 13.10.2015 21:46

«Слишком много 'const' в API, когда оно не нужно, похоже на« плачущий волк », в конечном итоге люди начнут игнорировать 'const', потому что он повсюду и большую часть времени ничего не значит« Что? Суть константы в том, что ее нельзя игнорировать, потому что компилятор не позволит вам ее игнорировать.

Taywee 16.06.2016 08:36

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

Adisak 19.07.2016 00:03

Это, безусловно, помогает выявлять тонкие ошибки, к которым склонны допускать люди, особенно когда использование констант согласовано в кодовой базе. один например. будет функцией std::stringc_str(), которая возвращает const char * для базовой строки. Кто-то ошибочно изменял передаваемую строку, когда это не предназначалось. Нас спасли компиляторы.

Abhinav Gauniyal 02.09.2016 20:06

Добавление const к параметру передачи по значению загрязняет API реализацией. Он ничего не сообщает вызывающему и (как говорит @Adisak) загромождает API, затрудняя выбор важных частей. Мы никогда не разрешаем изменять параметры функции передачи по значению в теле, и это одна из ключевых проверок при проверке кода (вы делаете проверки кода?). Если вы хотите изменить значение, сделайте копию (и вы можете дать ей имя, которое документирует ваше использование).

Ian Brockbank 07.10.2016 12:16

@AbhinavGauniyal, используя "const char *" для строки, символы которой не должны изменяться, - это разумно. Это необходимо const. Однако примером ненужной константы в API может быть использование «const char * const» для той же строки или, что еще хуже, использование «char * const» для строки, символы которой функция изменила.

Adisak 07.10.2016 18:14

@IanBrockbank: да, обзоры кода важны для меня, настолько важны, что есть две итерации, одна компилятором, а другая - людьми. Пример, который я привел, был первым, и он отлично подходит для меня.

Abhinav Gauniyal 07.10.2016 18:51

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

Don Hatch 14.10.2016 00:04

Этот ответ бесполезен. По сути, это длинная и многословная тирада о том, что никто не делает и не предлагает делать. Это классический аргумент соломенного человека.

melpomene 12.02.2017 01:37

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

Adisak 13.02.2017 02:47

@Adisak Вопрос был про const по параметрам в определении функции. Первые три четверти вашего ответа посвящены const в API, то есть объявлениям функций. Есть небольшое отступление об исключении const из объявления, но затем оно уходит по касательной и говорит об исключении const только из определения (WTF?). Последний квартал посвящен тому, как создание переменной const означает, что вы не можете ее изменить, и если вам нужно, вы должны либо удалить const, либо сделать изменяемую копию. Классная история, но опять же не имеющая отношения к API или их разработчикам.

melpomene 13.02.2017 04:06

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

Adisak 13.02.2017 08:24

@Adisak Никто не предлагал использовать const в API. Ваш «более широкий ответ» просто не отвечает на вопрос.

melpomene 13.02.2017 11:19

@melpomene Редко можно встретить первичную критику в ответ, потому что он слишком глубоко освещает тему, но не стесняйтесь писать свой собственный ответ с гораздо более узкими рамками, если вы считаете, что существует более правильный и сжатый ответ. Я оставлю это, потому что многие другие сочли его полезным.

Adisak 14.02.2017 00:52

@Adisak В четвертый раз ваш ответ не затрагивает «тему»; он полностью пропускает тему. Мне не нравится, как вы пытаетесь искажать мои слова. (Он «охватывает» совершенно другую тему, но не очень хорошо и глубоко; это много повторений и эмоционально заряженных слов (беспорядок, фильм плохой парень, глупый, ложь, ужасная вещь, ужасная ложь, бельмо на глазу, ...), но очень поверхностный техническое содержание.)

melpomene 14.02.2017 01:32

@melpomene Вы читали тот же текст, что и я? Кажется, вы ограничиваете следующий текст API только там, где, кажется, задается что-то более широкое: Как далеко вы зашли с const? Вы просто делаете функции константными, когда это необходимо, или вы делаете все возможное и используете его везде? Лично я предпочитаю широко использовать его, включая параметры, но в этом случае мне интересно, стоит ли оно того? - и мой ответ - используйте const столько, сколько вам нужно, но не тогда, когда это не нужно или вводит в заблуждение

Adisak 15.02.2017 01:59

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

dom_beau 27.03.2017 07:07

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

Ryan Lundy 06.11.2017 12:17

Решение "лишних констант" #define CDa const* #define CP *const #define CPD const * const * указатель на CONSTANT DATA * CONSTANT POINTER на данные * CONSTANT указатель на CONSTANT данные *** void mungerum(char CP buffer, char CPD mask, CN int count); vs void mungerum(char * const buffer, const char * const mask, const int count); Также устраняет путаницу с использованием const перед типом и после типа и обеспечивает меньшую многословность.

slyy2048 27.07.2018 13:05

На мой взгляд, это лучший ответ, и в этом случае руководство по стилю Google также соглашается с ним: google.github.io/styleguide/cppguide.html#Use_of_const: «Для параметра функции, переданного по значению, const не влияет на вызывающую программу, поэтому не рекомендуется в объявлениях функций. См. TotW # 109

Gabriel Staples 24.03.2020 02:06

Я решил также добавить свой собственный ответ, который добавляет мои собственные слова, поддерживает @ Adisak, answer и цитирует Руководство по стилю Google C++ по этому вопросу: stackoverflow.com/questions/117293/….

Gabriel Staples 24.03.2020 02:43

Для первой части вашего ответа см. stackoverflow.com/questions/1143262/…, чтобы понять, почему реализация 1 не работает, но 2 можно скомпилировать. Но это все еще проблема реализации, а не API.

Th. Thielemann 28.05.2020 15:54

Если вы используете операторы ->* или .*, это обязательно.

Это мешает вам написать что-то вроде

void foo(Bar *p) { if (++p->*member > 0) { ... } }

что я почти сделал прямо сейчас, и который, вероятно, не делает то, что вы намереваетесь.

То, что я хотел сказать, было

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

и если бы я поставил const между Bar * и p, компилятор сказал бы мне это.

Я бы немедленно проверил ссылку на приоритет операторов, когда я собираюсь смешать вместе такое количество операторов (если я еще не знаю 100%), так что IMO это не проблема.

mk12 26.08.2012 06:15

Я бы разбил эту 1 сложную строку примерно на 5 или более кристально чистых и легко читаемых строк, каждая из которых имеет описательное имя переменной, которое делает всю эту операцию самодокументированной. Так что для меня это не проблема. Сжимать код в одну строку, когда ухудшается читаемость и закрадываются ошибки, на мой взгляд, не лучшая идея.

Gabriel Staples 26.02.2021 19:31

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

Я знаю, что вопрос "немного" устарел, но когда я наткнулся на него, кто-то еще может сделать это в будущем ... ... все же я сомневаюсь, что бедняга перечислит здесь, чтобы прочитать мой комментарий :)

Мне кажется, что мы все еще слишком ограничены мышлением в стиле C. В парадигме ООП мы играем с объектами, а не с типами. Константный объект может концептуально отличаться от неконстантного объекта, в частности, в смысле логической константы (в отличие от побитовой константы). Таким образом, даже если постоянная правильность параметров функции является (возможно) чрезмерной осторожностью в случае POD, это не так в случае объектов. Если функция работает с константным объектом, это должно быть сказано об этом. Рассмотрим следующий фрагмент кода

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps: вы можете возразить, что ссылка (const) была бы более подходящей здесь и дает вам такое же поведение. Ну да ладно. Просто дать картину, отличную от того, что я мог видеть в другом месте ...

Будучи программистом VB.NET, которому необходимо использовать программу на C++ с более чем 50 открытыми функциями и файл .h, который время от времени использует квалификатор const, трудно понять, когда обращаться к переменной с помощью ByRef или ByVal.

Конечно, программа сообщает вам, генерируя ошибку исключения в строке, где вы допустили ошибку, но тогда вам нужно угадать, какой из 2-10 параметров неверен.

Итак, теперь у меня есть неприятная задача - попытаться убедить разработчика в том, что они действительно должны определять свои переменные (в файле .h) таким образом, чтобы можно было легко использовать автоматизированный метод создания всех определений функций VB.NET. Затем они самодовольно скажут: «Прочтите ... документацию».

Я написал сценарий awk, который анализирует файл .h и создает все команды Declare Function, но без индикатора того, какие переменные являются R / O или R / W, он выполняет только половину работы.

Обновлено:

По просьбе другого пользователя я добавляю следующее:

Вот пример (IMO) плохо сформированной записи .h;

typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql );

Результирующий VB из моего скрипта;

    Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer

Обратите внимание на отсутствие «const» в первом параметре. Без него программа (или другой разработчик) не имеет идеи, в качестве 1-го параметра следует передать «ByVal». Добавление «const» делает файл .h самодокументированным, так что разработчики, использующие другие языки, могут легко писать рабочий код.

@anatolyg, вы правы, это напыщенная речь, но она выявляет проблему с неправильным использованием концепций на языке, которые могут не потребоваться, но обеспечивают удобство использования и самостоятельную документацию. Если вы пишете код, который требует использования других разработчиков, важно сделать его пригодным для использования. Если кто-то сочтет это полезным, я с удовольствием опубликую сценарий AWK, но он работает только в том случае, если файл .h правильно сформирован.

Paul Stearns 28.05.2017 22:50

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

anatolyg 29.05.2017 11:21

@anatolyg, так лучше?

Paul Stearns 29.05.2017 14:45

По крайней мере, теперь я понимаю, что вы пытаетесь сказать! Но я не согласен: и int smfID, и const int smfID означают «передачу по значению» в C++, а int& smfID означает «передачу по ссылке».

anatolyg 29.05.2017 16:42

@anatolyg В файле .h нет символа «&», и эти параметры, похоже, взаимозаменяемы для ByRef / ByVal. Единственный способ узнать это - либо поискать в документации .CHM-файла, либо подождать, пока моя программа не выйдет из строя с ошибкой «System.AccessViolationException», и попытаться выяснить, какой это параметр. Конкретная dll, которую я сейчас использую, имеет ~ 100 общедоступных методов, и я хотел бы автоматизировать создание «функций объявления».

Paul Stearns 29.05.2017 17:45

Подвести итоги:

  • «Обычно передача констант по значению бесполезна и в лучшем случае вводит в заблуждение». От GOTW006
  • Но вы можете добавить их в .cpp так же, как и с переменными.
  • Обратите внимание, что стандартная библиотека не использует const. Например. std::vector::at(size_type pos). То, что достаточно для стандартной библиотеки, хорошо для меня.

«То, что достаточно хорошо для стандартной библиотеки, хорошо для меня» - не всегда верно. Например, стандартная библиотека постоянно использует уродливые имена переменных вроде _Tmp - вам это не нужно (на самом деле вам не разрешено их использовать).

anatolyg 27.07.2017 10:44

@anatolyg это деталь реализации

Fernando Pelliccioni 20.09.2017 19:41

Хорошо, и имена переменных, и типы с указанием const в списках аргументов являются деталями реализации. Я хочу сказать, что реализация стандартной библиотеки иногда бывает плохой. Иногда вы можете (и должны) добиться большего. Когда был написан код стандартной библиотеки - 10 лет назад? 5 лет назад (какие-то новейшие его части)? Сегодня мы можем писать лучший код.

anatolyg 23.09.2017 15:47

Другие вопросы по теме