В этом есть несколько частей, которые позволяют всем этим комбинациям операторов работать одинаково.
Основная причина, по которой все эти работы заключаются в том, что функция (например foo) неявно преобразуется в указатель на функцию. Вот почему void (*p1_foo)() = foo;работает: fooнеявно преобразуется в указатель на себя и этот указатель назначается p1_foo.
Унарный &, при применении к функции, выдает указатель на функцию, точно так же, как и адрес объекта, когда он применяется к объекту. Для указателей на обычные функции он всегда избыточен из-за неявного преобразования функции в указатель на функцию. В любом случае именно поэтому и void (*p3_foo)() = &foo;работает.
Унарный *, при применении к указателю на функцию, возвращает указанную функцию, точно так же, как и на указательный объект, когда он применяется к обычному указателю на объект.
Эти правила могут быть объединены. Рассмотрим ваш второй до последнего примера **foo:
- Во-первых,
fooнеявно преобразуется в указатель на себя, а первый *применяется к этому указателю функции, fooснова получая функцию .
- Затем результат снова неявно преобразуется в указатель на себя, и применяется второе
*, снова получая функцию foo.
- Затем он снова неявно преобразуется в указатель функции и присваивается переменной.
Вы можете добавить столько *s, сколько захотите, результат всегда одинаков. Чем больше *с, тем лучше.
Мы также можем рассмотреть ваш пятый пример &*foo:
- Во-первых,
fooнеявно преобразуется в указатель на себя; применяется унарный *, fooснова уступающий .
- Затем
&применяется к foo, давая указатель на foo, который присваивается переменной.
&Может быть применен только к функции , хотя, а не к функции , которая была преобразована в указатель функции (если, конечно, указатель функция не является переменной, и в этом случае результатом является указатель на а-pointer- к функции, например, вы можете добавить в свой список void (**pp_foo)() = &p7_foo;).
Вот почему &&fooне работает: &fooэто не функция; это указатель на функцию, который является rvalue. Однако &*&*&*&*&*&*fooбудет работать, как и &******&fooпотому, что в обоих этих выражениях &всегда применяется к функции, а не к указателю функции rvalue.
Также обратите внимание, что вам не нужно использовать унарный код *для вызова через указатель функции; оба (*p1_foo)();и (p1_foo)();имеют одинаковый результат, опять же из-за преобразования функции в указатель функции.
&fooпринимает адресfoo, в результате чего указатель на функцию указываетfoo, как и следовало ожидать.