Интересно, что , используя имя функции в качестве указателя на функцию, эквивалентно применению адреса оператора к имени функции!
Вот пример.
typedef bool (*FunType)(int);
bool f(int);
int main() {
FunType a = f;
FunType b = &a; // Sure, here an error.
FunType c = &f; // This is not an error, though.
// It equivalent to the statement without "&".
// So we have c equals a.
return 0;
}
Использование имени - это то, что мы уже знаем в массиве. Но вы не можете написать что-то вроде
int a[2];
int * b = &a; // Error!
Кажется, это не соответствует другим частям языка. Какое обоснование этого дизайна?
Этот вопрос объясняет семантику такого поведения и почему он работает. Но меня интересует, почему язык был разработан таким образом.
Что более интересно, тип функции может быть неявно преобразован в указатель на себя при использовании в качестве параметра, но не будет преобразован в указатель на себя при использовании в качестве возвращаемого типа!
Пример:
typedef bool FunctionType(int);
void g(FunctionType); // Implicitly converted to void g(FunctionType *).
FunctionType h(); // Error!
FunctionType * j(); // Return a function pointer to a function
// that has the type of bool(int).
Ответ 1
Поскольку вы специально задаете обоснование этого поведения, вот ближайшая вещь, которую я могу найти (из документа обоснования ANSI C90 - http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):
3.3.2.2 Функциональные вызовы
Указатели на функции могут использоваться как (*pf)()
или как pf()
. Последняя конструкция, не санкционированная в базовом документе, появляется в некоторые настоящие версии C, недвусмысленны, недействительны для старого кода, и может быть важной стенографией. Сокращение полезно для пакеты, которые содержат только одно внешнее имя, которое обозначает структура, полная указателей на объект s и функции: член функции можно назвать graphics.open(file)
вместо (*graphics.open)(file)
. Обработка указателей функций может приводят к некоторым любопытным, но действительным синтаксическим формам. Учитывая объявления:
int f ( ) , ( *pf ) ( ) ;
тогда все следующие выражения являются действительными вызовами функций:
( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();
Первое выражение в каждой строке обсуждалось в предыдущем параграф. Второе - обычное использование. Все последующие выражения используют неявное преобразование функции указатель на значение указателя, почти во всех контекстах выражения. Комитет не видел реального вреда в разрешении этих форм; вне закона формы, такие как (*f)()
, но при этом разрешая *a
(для int a[])
, просто казалось больше неприятностей, чем это стоило.
В принципе, эквивалентность между указателями функций и указателями функций была добавлена, чтобы сделать использование указателей функций немного более удобным.
Ответ 2
Это функция, унаследованная от C.
В C это допускалось прежде всего потому, что не могло бы означать имя функции, само по себе. Все, что вы можете сделать с помощью фактической функции, это вызвать ее. Если вы его не называете, единственное, что вы можете сделать, это взять адрес. Поскольку нет никакой двусмысленности, в любое время после имени функции не следует (
, чтобы обозначить вызов функции, имя оценивается по адресу функции.
Это фактически похоже на одну другую часть языка - имя массива оценивается по адресу первого элемента массива, за исключением некоторых довольно ограниченных обстоятельств (используется как операнд &
или sizeof
).
Так как C разрешил это, С++ также делает это, главным образом потому, что то же самое остается правдой: единственное, что вы можете сделать с помощью функции, - это вызвать его или принять его адрес, поэтому, если вместо имени не следует (
для обозначения вызова функции, тогда имя оценивается по адресу без двусмысленности.
Ответ 3
Для массивов отсутствует распад указателя при использовании адреса оператора:
int a[2];
int * p1 = a; // No address-of operator, so type is int*
int (*p2)[2] = &a; // Address-of operator used, so type is int (*)[2]
Это имеет смысл, потому что массивы и указатели являются разными типами, и, например, можно возвращать ссылки на массивы или передавать ссылки на массивы в функциях.
Однако, с функциями, какой другой тип может быть возможен?
void foo(){}
&foo; // #1
foo; // #2
Предположим, что только # 2 дает тип void(*)()
, каков будет тип &foo
? Нет другой возможности.