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

StackOverflow https://stackoverflow.com/questions/1314613

  •  19-09-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

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

Редактировать: Имажинист спрашивает:

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top