Почему использование имени функции в качестве указателя на функцию эквивалентно применению оператора address-of к имени функции?
Интересно, что использование имени функции в качестве указателя на функцию эквивалентно применению оператора 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;
}
Использование имени-это то, что мы уже знаем в array. Но вы не можете написать что-то вроде
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 Rationality document - http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2):
3.3.2.2 вызовы функций
Указатели на функции могут использоваться либо как(*pf)()
, либо какpf()
. Последняя конструкция, не санкционированная в базовом документе, появляется в некоторые из существующих версий C, является однозначным, не делает недействительным ни один старый код, и может быть важно стенографически. Стенографии является полезным для пакеты, представляющие только одно внешнее имя, которое обозначает структура, полная указателей на объект s и функции: member функции могут быть вызваны как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]
Это имеет смысл, поскольку массивы и указатели являются различными типами, и можно, например, возвращать ссылки на массивы или передавать ссылки на массивы в функциях.
Однако, с функциями, какой другой тип может быть возможен?
Давайте представим, что только #2 дает типvoid foo(){} &foo; // #1 foo; // #2
void(*)()
, Каким будет тип&foo
? Другой возможности нет.