Почему использование имени функции в качестве указателя на функцию эквивалентно применению оператора адреса к имени функции?
Интересно, что использование имени функции в качестве указателя на функцию эквивалентно применению оператора 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
быть? Там нет другой возможности.