Почему использование имени функции в качестве указателя на функцию эквивалентно применению оператора 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 13

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]

Это имеет смысл, поскольку массивы и указатели являются различными типами, и можно, например, возвращать ссылки на массивы или передавать ссылки на массивы в функциях.

Однако, с функциями, какой другой тип может быть возможен?

void foo(){}
&foo; // #1
foo;  // #2
Давайте представим, что только #2 дает тип void(*)(), Каким будет тип &foo? Другой возможности нет.