Почему использование имени функции в качестве указателя на функцию эквивалентно применению оператора 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; // #2void(*)(), Каким будет тип&foo? Другой возможности нет.