В стандарте C отсутствует положение о перегрузке оператора; Предложения по его добавлению были отклонены на том основании, что многие системы сборки не имеют возможности для размещения нескольких функций с одинаковым именем. Хотя C ++ может обойти это, например, имеющий
void foo(int);
int foo(char*);
long foo(char *, char **);
компилировать в функции с именами типа v__foo_i, i__foo_pc и l__foo_pc_ppc [компиляторы используют разные соглашения об именах, хотя стандарт C ++ запрещает использование внутренних двойных подчеркиваний в идентификаторах, чтобы позволить компиляторам давать имена вещей, подобные приведенным выше, без конфликта ]. Авторы стандарта C не хотели требовать, чтобы какие-либо компиляторы изменили соглашения об именах, чтобы разрешить перегрузку, поэтому они не предусматривают это.
Было бы возможно и полезно для компилятора разрешить перегрузку статических и встроенных функций без создания проблем с именами; на практике это было бы так же полезно, как и перегрузка внешне-связываемых функций, поскольку можно иметь заголовочный файл:
void foo_zz1(int);
int foo_zz2(char*);
long foo_zz3(char *, char **);
inline void foo(int x) { foo_zz1(x); }
inline int foo(char* st) { foo_zz2(st); }
long foo(char *p1, char **p2) { foo_zz3(p1,p2); }
Я вспоминаю, как смотрел на встроенный компилятор для гибрида между C и C ++, который поддерживал вышеупомянутое как нестандартное расширение, но я не уверен в деталях. В любом случае, даже если некоторые компиляторы C поддерживают перегрузку функций, которые не имеют внешней связи, это не поддерживается C14, и я не знаю (к сожалению) каких-либо активных попыток добавить такую функцию в будущие стандарты C.
Тем не менее, GCC может быть сделан с использованием макросов для поддержки формы перегрузки, которая не поддерживается напрямую в языках с перегрузкой операторов. GCC включает в себя встроенную функцию, которая будет определять, может ли выражение быть оценено как константа времени компиляции. Используя эту встроенную функцию, можно написать макрос, который может оценивать выражение различными способами (в том числе путем вызова функций) в зависимости от аргумента. Это может быть полезно в некоторых случаях, когда формула будет оцениваться как константа времени компиляции, если дан аргумент константы времени компиляции, но приведет к ужасному беспорядку, если дан аргумент переменной. В качестве простого примера, предположим, что кто-то хочет перевернуть 32-битное значение. Если значение постоянное, это можно сделать с помощью:
#define nyb_swap(x) \
((((x) & 1)<<3) | (((x) & 2)<<1) | (((x) & 4)>>1) | ((((x) & 8)>>3) )
#define byte_swap(x) \
( (nyb_swap(x)<<4) | nyb_swap((x) >> 4) )
#define word_swap(x) \
( (byte_swap(x)<<24) | (byte_swap((x) >> 8)<<16) | \
(byte_swap((x) >> 16)<<8) | (byte_swap((x) >> 24)) )
И выражение типа uint32_t x=word_swap(0x12345678);
просто загрузит x
с 0x87654321. С другой стороны, если значение не является константой, результат будет ужасным: выражение типа uint32_t y=word_swap(x);
может генерировать множество десятков инструкций; вызов функции с частично развернутым циклом будет почти таким же быстрым, но намного более компактным. С другой стороны, использование цикла предотвратит рассмотрение результата как константы времени компиляции.
Используя GCC, можно определить макрос, который будет использовать макрос, дающий константу, если ему присвоена константа, или вызвать функцию, если ему дана переменная:
#define wswap(x) \
(__builtin_constant_p((x)) ? word_swap((x)) : word_swap_func((x))
Этот подход не может сделать все, что может сделать перегрузка на основе типов, но он может сделать многое из того, что перегрузка не может.