Почему использование имени функции в качестве указателя функции эквивалентно применению оператора адреса к имени функции?

Интересно, что , используя имя функции в качестве указателя на функцию, эквивалентно применению адреса оператора к имени функции!

Вот пример.

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? Нет другой возможности.