Каковы проблемы приведения C ++ - подобного const в язык?

17

Меня интересует идея C ++ - например, constне это конкретное выполнение (например, отбрасывание const).

Возьмем, к примеру, C # - ему не хватает C ++ - как const, и причина для этого обычная - люди и время. Здесь, кроме того, кажется, что команда C # посмотрела на выполнение C ++ const, маркетинговую CLR и на этом хватило (посмотрите, почему в c # и параметре const нет метода const member ; спасибо, Свик). Будь, если мы продвинемся дальше, что-нибудь еще?

Есть ли что-то более глубокое? Возьмем, к примеру, множественное наследование - его обычно трудно понять (для пользователя), и поэтому оно не добавляется в язык (как проблема с алмазом).

Есть ли что - то в ПРИРОДЕ из constэтой позы проблемы , что лучше для языка , чтобы избежать этого? Что-то вроде принятия решения, constдолжен ли он быть глубоким или неглубоким (наличие constконтейнера означает, что я не могу добавить новый элемент или изменить существующие элементы; что, если элементы относятся к ссылочному типу)?

ОБНОВЛЕНИЕ : хотя я упоминаю C # и, таким образом, делаю историческую перспективу, меня интересует характер потенциальных проблем constязыка.

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

greenoldman
источник
3
в качестве отступления: множественное наследование может быть нетрудно понять, но разрешение метода может стать довольно уродливым. Наивное разрешение в глубину быстро становится неинтуитивным (« проблема с бриллиантами »). Порядок разрешения метод C3 (опубл '96) решает эту проблему, но, конечно , не легко понять. Поэтому запрет на множественное наследование часто может показаться весьма разумным, но реальная проблема заключается в том, что Java предшествует алгоритму C3 и ориентируется на C # после Java.
Амон
3
@amon Многие люди на самом деле не думают, что множественное наследование - это особенность, которую вы хотите иметь в первую очередь, например создатели языка программирования D: это сложная особенность дискуссионной ценности. Это очень сложно реализовать эффективным способом, и компиляторы склонны к множеству ошибок при его реализации. Почти все значения MI могут обрабатываться с помощью одного наследования в сочетании с интерфейсами и агрегацией. То, что осталось, не оправдывает вес реализации MI. (источник)
Jcora
5
Дубликат stackoverflow.com/q/4150478/41071 .
свик
2
Даже без множественного наследования вы уже можете закодировать любую проблему 3-SAT как разрешение метода (или, точнее, разрешение перегрузки) в C #, что делает его NP-завершенным.
Йорг Миттаг
1
@ Свик, большое спасибо. Поскольку у меня не было ответов на этот вопрос, я позволил себе отредактировать его, чтобы сосредоточиться на природе проблемы, а не на исторических причинах, потому что кажется, что 99% из них были «время + люди + анти-C ++ предвзятость». + CLR».
Гринольдман

Ответы:

10

Обратите внимание, если это подходит для вас, но в функциональных языках, таких как Standard ML, все по умолчанию неизменяемо. Мутация поддерживается через универсальный refтип erence. Таким образом, intпеременная является неизменной, а ref intпеременная является изменяемым контейнером для ints. По сути, переменные - это реальные переменные в математическом смысле (неизвестное, но фиксированное значение), а refs - это «переменные» в смысле императивного программирования - ячейка памяти, в которую можно записывать и читать. (Мне нравится называть их назначаемыми .)

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

Во-вторых, в C ++ вам нужно выбирать, constа не отказываться от него. Но когда вы constчто-то забудете, а потом исправите, вы окажетесь в ситуации «отравления константой», упомянутой в ответе @ RobY, где constизменение будет касаться всего кода. Если бы это constбыло по умолчанию, вы бы не подали заявку constзадним числом. Кроме того, необходимость добавлять constвезде добавляет много шума в код.

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

РЕДАКТИРОВАТЬ : После размышления на комментарии Greenoldman я понял, что constэто не напрямую об неизменности данных; constкодирует в тип метода, имеет ли он побочные эффекты на экземпляре.

Возможно использование мутации для достижения ссылочно прозрачного поведения. Предположим, у вас есть функция, которая при последовательном вызове возвращает разные значения - например, функция, которая читает один символ stdin. Мы могли бы использовать кеш / запоминать результаты этой функции для создания ссылочно прозрачного потока значений. Поток будет связанным списком, чьи узлы будут вызывать функцию при первой попытке получить их значение, но затем кэшировать результат. Так что, если stdinconstains Hello, world!, первый раз , когда вы пытаетесь получить значение первого узла, он будет прочитать одно charи возвращение H. После этого он продолжит возвращаться Hбез дальнейших звонков, чтобы прочитать char. Аналогично, второй узел будет читать charизstdinв первый раз вы пытаетесь получить его значение, на этот раз возвращаете eи кешируете этот результат.

Интересно то, что вы превратили процесс, который по своей сути является состоянием, в объект, который кажется не имеющим состояния. Однако, чтобы достичь этого, необходимо было изменить внутреннее состояние объекта (путем кэширования результатов) - мутация была благоприятным эффектом . Невозможно сделать наш, CharStream constдаже если поток ведет себя как неизменное значение. Теперь представьте, что есть Streamинтерфейс с constметодами, и все ваши функции ожидают const Streams. Вы CharStreamне можете реализовать интерфейс!

( РЕДАКТИРОВАТЬ 2: Очевидно, есть ключевое слово C ++ mutable, которое позволит нам обманывать и делатьCharStream const . Однако, эта лазейка разрушает constгарантии - теперь вы действительно не можете быть уверены, что что-то не будет мутировать через его constметоды. Я полагаю, что это не так плохо, так как вы должны явно просить лазейку, но вы все еще полностью полагаетесь на систему чести.)

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

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

Мне не ясно, что кодирование наличия или отсутствия побочных эффектов в системе типов - это всегда хорошая вещь.

Doval
источник
1
+1 спасибо. Неизменяемые объекты - это нечто иное, чем C ++ const (это скорее представление). Но скажем, C ++ будет иметь const по умолчанию, и varпо требованию оставит только одну проблему - глубина?
Гринольдман
1
@greenoldman Хорошая мысль. Прошло много времени с тех пор, как я использовал C ++, поэтому я забыл, что у вас может быть constссылка на constнеобъект. Несмотря на это, я не думаю, что имеет смысл иметь мелководье const. Весь смысл в constтом, что вы не сможете изменить объект с помощью этой ссылки. Вы не можете обойтись const, создав не constссылку, так почему было бы нормально обойти constпутем изменения членов объекта?
Доваль
+1 :-) Очень интересные проблемы в вашем обновлении (жизнь с не / const лямбдами и, возможно, более слабая проблема с внешним состоянием), я должен переварить это дольше. Во всяком случае, что касается вашего комментария, я не имею в виду ничего плохого - наоборот, я ищу функции для повышения ясности кода (еще один: ненулевые указатели). Та же категория здесь (по крайней мере, это моя цель) - я определяю, func foo(const String &s)и это сигнал sне будет изменен в foo.
Гринольдман
1
@greenoldman Я определенно вижу привлекательность и обоснование. Тем не менее, я не думаю, что есть реальное решение проблемы, кроме как избежать максимально возможного изменения состояния (наряду с другими неприятными вещами, такими как nullнаследование).
Doval
8

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

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

Карл Билефельдт
источник
+1 спасибо. Хотя неизменяемый объект - это нечто иное, чем я ищу (вы можете сделать объект неизменным в C # или Java), даже при неизменном подходе вы должны решить, является ли он глубоким или неглубоким, возьмите, например, контейнер указателей / ссылок (можете ли вы изменить значение объектов, на которые указывают). По истечении дня кажется, что основная проблема заключается в том, что установлено по умолчанию, и это легко решаемо при разработке нового языка.
Гринольдман
1
+1 за lolz @ "Некоторые люди предпочитают, чтобы их компилятор не помог им. Я не понимаю этих людей, но их много".
Томас Эдинг
6

Я вижу недостатки как:

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

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

  • в целом, настроение отрасли, похоже, отходит от сильных гарантий во время компиляции, как бы много комфорта они ни принесли вам и мне. Так что в течение дня я трачу 66% своей кодовой базы из-за отравления констант, некоторого Python Парень выполнил 10 отлично работоспособных, хорошо спроектированных, хорошо протестированных, полностью функциональных модулей Python. И он даже не взломал, когда сделал это.

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

РЕДАКТИРОВАТЬ: краткий ответ, новый язык имеет крутой холм, чтобы подняться для принятия. Это дизайнеры действительно должны задуматься о том, какие функции он должен получить принятие. Возьми Go, например. Google как бы жестоко обрезал некоторые из самых глубоких религиозных баталий, делая несколько вариантов дизайна в стиле Конана-варвара, и я на самом деле думаю, что язык лучше для него, из того, что я видел.

обкрадывать
источник
2
Ваша вторая пуля неверна. В C ++ существует три / четыре способа объявления ссылки / указателя, относительно. constness: int *изменяемый указатель на изменяемое значение, int const *изменяемый указатель на постоянное значение, постоянный int * constуказатель на изменяемое значение, постоянный int const * constуказатель на постоянное значение.
Phresnel
Спасибо. Я спросил о природе, а не о маркетинге («уход индустрии» не является природой C ++ const), поэтому такие «причины» я считаю неактуальными (по крайней мере, для этого вопроса). Про constотравление - можете привести пример? Потому что AFAIK это преимущество, вы что-то пропускаете constи оно должно остаться const.
Гринольдман
1
Это не constпроблема, но изменение типа действительно - что бы вы ни делали, это всегда будет проблемой. Подумайте о прохождении RichString, а потом понимаете, что вам лучше пройти String.
Greenoldman
4
@RobY: Я не уверен, что / с кем вы обращаетесь к последним и бывшим . Тем не менее: после того дня, пройдя по коду, вы должны были узнать, что вам следует рефакторинг, чтобы он соответствовал константной корректности снизу вверх, а не сверху вниз, то есть начинал с самых внутренних модулей и продолжал идти вверх. Возможно, ваши отряды также недостаточно изолированы, вместо этого они тесно связаны, или вы стали жертвой внутренних систем.
Phresnel
7
«Отравление константой» на самом деле не проблема - реальная проблема в том, что C ++ по умолчанию не-non const. Это слишком легко, чтобы не делать constвещи, которые должны быть, constпотому что вы должны согласиться на это, а не на это.
Доваль
0

Есть некоторые философские проблемы в добавлении constк языку. Если вы объявляете constклассу, это означает, что класс не может измениться. Но класс - это набор переменных, связанных с методами (или мутаторами). Мы достигаем абстракции, скрывая состояние класса за набором методов. Если класс предназначен для сокрытия состояния, тогда вам нужны мутаторы, чтобы изменить это состояние извне, и тогда его уже constнет. Таким образом, можно объявить constтолько неизменяемые классы. Так что constкажется возможным только в сценариях, где классы (или типы, которые вы хотите объявить const) тривиальны ...

Так что нам нужно в constлюбом случае? Я так не думаю. Я думаю, что вы можете легко обойтись без constхорошего дизайна. Какие данные вам нужны и где, какие методы являются методами данных к данным и какие абстракции вам нужны для ввода-вывода, сети, представления и т. Д. Затем вы естественным образом разделяете модули и классы, которые имеют дело с состоянием, и у вас есть методы и неизменные классы, которые не нуждаются в состоянии.

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

Я знаю, что в C ++ можно объявлять некоторые методы constи не объявлять другие, потому что они являются мутаторами. И потом, через некоторое время вам нужен кеш, а затем получатель мутирует объект (потому что он загружается лениво), и это constбольше не так . Но в этом весь смысл этой абстракции, верно? Вы не знаете, какое состояние мутирует с каждым звонком.

расточитель
источник
1
«Таким образом, const представляется возможным только в сценариях, где классы (или типы, которые вы хотите объявить как const) тривиальны ...» Постоянные структуры данных неизменны и нетривиальны. Вы просто не увидите их в C ++, потому что почти все они совместно используют свои внутренние данные между несколькими экземплярами, а в C ++ отсутствует сборщик мусора, чтобы это работало. «Я думаю, что вы можете легко обойтись без const, если у вас есть хороший дизайн». Неизменные значения делают код намного проще для размышления.
Доваль
+1 спасибо. Не могли бы вы уточнить «объявить const классу»? Вы подразумевали установку класса как const? Если да, я не спрашиваю об этом - const в C ++ работает как представление, вы можете объявить const рядом с объектом, а не с классом (или что-то изменилось в C ++ 11). Также есть проблема со следующим абзацем и словом «легко» - есть хороший вопрос о C ++ - const в C #, и объем работы, который он включает, является первостепенным - для каждого типа, который вы намереваетесь поместить const(в смысле C ++ ) вы должны определить интерфейс, также для всех свойств.
Гринольдман
1
Когда вы говорите о «классе», вы на самом деле имеете в виду «экземпляр класса», верно? Я думаю, что это важное различие.
svick