Есть ли причина, по которой C99 не поддерживает перегрузку функций? - PullRequest
6 голосов
/ 22 августа 2009

Очевидно (по крайней мере, согласно gcc -std=c99) C99 не поддерживает перегрузку функций. Причина, по которой не поддерживается какая-то новая функция в C, обычно заключается в обратной совместимости, но в этом случае я не могу вспомнить ни одного случая, когда перегрузка функций нарушила бы обратную совместимость. В чем причина отсутствия этой базовой функции?

Ответы [ 3 ]

27 голосов
/ 22 августа 2009

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

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

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

В C функции становятся именами символов для компоновщика, поэтому, когда вы пишете

int main(int argc, char **argv) { return 1; }

компилятор предоставляет архив объектного кода, который содержит объект с именем main.

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

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

Недостатком этого является то, что имена символов не похожи на оригинальные имена функций. В частности, почти невозможно предсказать, каким будет имя перегруженной функции, чтобы вы могли ссылаться на нее. Чтобы связать с внешним кодом, вы можете использовать extern "C", что заставляет эти функции следовать стилю имен символов C, но, конечно, вы не можете перегружать такую ​​функцию.

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

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

Редактировать : Imagist спрашивает:

Действительно ли это будет менее портативным или труднее взаимодействовать с функция, если вы разрешили int main (int argc, char ** argv) что-то вроде main-int-int-char ** вместо основного (и это было частью стандарта)? Я не вижу здесь проблемы. По факту, мне кажется, что это дает вам больше информации (которая может быть использована для оптимизации и тому подобное)

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

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

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

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

19 голосов
/ 22 августа 2009

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

Кроме того, C не поддерживает строгую типизацию. Многие вещи неявно преобразуются друг в друга в C. Сложность правил разрешения перегрузки может привести к путанице в такого рода языках.

6 голосов
/ 24 августа 2009

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

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

...