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

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

Вот пример.

typedef bool (*FunType)(int);
bool f(int);
int main() {
  FunType a = f;
  FunType b = &a; // Sure, here's an error.
  FunType c = &f; // This is not an error, though. 
                  // It's 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).

3 ответа

Решение

Поскольку вы специально спрашиваете об обосновании такого поведения, вот что я могу найти (из документа ANSI C90 Rationale - http://www.lysator.liu.se/c/rat/c3.html):

3.3.2.2 Вызовы функций

Указатели на функции могут использоваться как (*pf)() или как pf(), Последняя конструкция, не разрешенная в Базовом документе, появляется в некоторых существующих версиях C, недвусмысленна, не делает недействительным старый код и может быть важным сокращением. Сокращение полезно для пакетов, которые представляют только одно внешнее имя, которое обозначает структуру, полную указателей на объекты и функции: функции-члены могут вызываться как graphics.open(file) вместо (*graphics.open)(file), Обработка указателей функций может привести к некоторым любопытным, но действительным, синтаксическим формам. Учитывая декларации:

int f ( ) , ( *pf ) ( ) ; 

тогда все следующие выражения являются допустимыми вызовами функций:

( &f)(); f(); (*f)(); (**f)(); (***f)();
pf(); (*pf)(); (**pf)(); (***pf)();

Первое выражение в каждой строке обсуждалось в предыдущем абзаце. Второе - обычное использование. Все последующие выражения используют неявное преобразование указателя функции в значение указателя почти во всех контекстах выражений. Комитет не видел реального вреда в разрешении этих форм; вне закона формы, такие как (*f)()пока еще разрешаю *a (за int a[])Просто казалось, больше проблем, чем оно того стоило.

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

Это особенность, унаследованная от C.

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

Это на самом деле чем-то похоже на другую часть языка - имя массива вычисляется по адресу первого элемента массива, за исключением некоторых довольно ограниченных обстоятельств (используемых в качестве операнда & или же sizeof).

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

Для массивов указатель затухания отсутствует при использовании оператора address-of:

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

Другие вопросы по тегам