Последствия typedef void FOO vs. #define FOO void в сигнатурах функций
Пройдя через некоторый исходный код, который сильно смешивает C и С++, я натолкнулся на следующее (слегка измененное для защиты работы компании, значение остается тем же):
/*
* Typedefs of void are synonymous with the void keyword in C,
* but not in C++. In order to support the use of MY_VOID
* in place of the void keyword to specify that a function takes no
* arguments, it must be a macro rather than a typedef.
*/
#define MY_VOID void
В чем разница между typedef void MY_VOID
и #define MY_VOID void
в этом конкретном контексте?
Я не считаю, что это дубликат этого вопроса, потому что он конкретно спрашивает о последствиях в отношении сигнатур функций, а не о гораздо более общем "разница".
Ответы
Ответ 1
Простая тестовая программа в С++ демонстрирует разницу:
typedef void VOID;
void f(VOID) {}
int main()
{
f();
}
В компиляции (как С++) она дает следующие ошибки:
prog.cpp:5:8: error: '<anonymous>' has incomplete type
void f(VOID) {}
^
prog.cpp:5:12: error: invalid use of 'VOID {aka void}'
void f(VOID) {}
^
prog.cpp: In function 'int main()':
prog.cpp:9:7: error: too few arguments to function 'void f(<type error>)'
f();
^
prog.cpp:5:6: note: declared here
void f(VOID) {}
^
который объясняет, что означает комментарий в коде. В частности, кажется, что typedef VOID
пытается быть типом, отличным от VOID
, когда он используется как тип параметра.
Ответ 2
Комментарий объясняет разницу. Учитывая псевдоним для void
:
typedef void MY_VOID;
Если вы попытаетесь использовать это вместо void
, чтобы указать, что функция не принимает никаких параметров:
int f(MY_VOID);
C разрешит это, но С++ не будет.
Итак, если вы действительно хотите сделать жизнь трудной для себя, написав код, который (а) действителен на обоих языках, и (b) использует псевдоним для этого конкретного использования void
, этот псевдоним должен быть макрос.
Ответ 3
Комментарий относится к коду, подобному этому:
typedef void my_void_t;
my_void_t foo(my_void_t); // Illegal.
С #define
это законно.
Ответ 4
Отрывок из языковых стандартов может сделать все лучше!
C99, 6.7.5.3/10:
Частный случай неименованного параметра типа void как единственного элемента в списке указывает, что функция не имеет параметров.
С++, 8.3.5/2:
Если предложение parameter-declaration пустое, функция не принимает аргументов. Список параметров (void) эквивалентен списку пустых параметров.
Разница очевидна. C имеет void
в качестве неназванного параметра типа void
, а С++ имеет идентификатор void
. Первый может быть typedef
-ed, последний не может.
Может быть интересно подумать о причинах этого. На самом деле, в С++ допустимо следующее, но незаконно в C:
void fn(int){
}
Поскольку С++ имеет концепцию неназванных (анонимных) параметров в определениях функций, а C не... Почти.
На самом деле один неопределенный параметр может присутствовать в определении функции C: один из типов void
.
Что определенно не имеет никакого смысла, поскольку для этого типа не может быть именованного параметра.
Помимо небольшого смысла, это определение void
в списке параметров может испортить неименованные параметры С++, так как это ничем не отличается от них. В определение можно было бы внести поправки, чтобы сделать его особым случаем очень-очень-очень неназванного параметра, который не имеет соответствующих именованных параметров и не может использоваться с другими именованными или неназванными параметрами и фактически не является параметром и...
Но я предполагаю, что вместо того, чтобы пытаться сказать что-то такое сумасшедшее, коммисия С++ просто решила полностью удалить материал "unnamed-void-type-parameter" и перейти со "списком специальных параметров". Я говорю о хорошем избавлении.
И стандарт C? Вероятно, он сохраняет свои странные 6.7.5.3/10 для соображений обратной совместимости...
Ответ 5
Вы можете использовать оба варианта: они делают одно и то же, поэтому единственная разница в том, что с помощью #define вы объявите новый тип макросом.