Указатель на параметр функции vs параметр функции?

Я хотел бы понять, в чем разница между двумя объявлениями, f1 и f2, ниже: В f1 я объявляю параметр указателем на функцию типа void(), как отличается объявление f2 от f1? Являются ли объявления эквивалентными? В main я могу вызвать оба из них с помощью прототипа void (). Я понимаю концепцию передачи по значению/указателю/ссылке, однако это функции и не совсем понимают разницу. Я не могу "изменить" функцию, переданную как параметр в f1... Спасибо!

PS: пришел к этому вопросу, когда натыкался на известную проблему Most Vexing Parsing:)

#include <iostream>

using namespace std;

void f1(void (*x)())
{
    x();
}

void f2(void x())
{
    x();
}

void g1()
{
    cout << "Invoking f1(g1())" << endl;
}

void g2()
{
    cout << "Invoking f2(g2())" << endl;
}


int main() 
{
    f1(g1);
    f2(g2);
}

Компиляция программы и выход

Invoking f1(g1())
Invoking f2(g2())

Ответ 1

Они эквивалентны. Вы запутались в неявном преобразовании указателя, которое происходит с аргументами.

Поскольку вы не можете передать функцию в качестве аргумента функции (вы ничего не можете делать с функциями в C, кроме вызова или принятия их адреса), компилятор молча изменяет аргумент в указатель на функцию.

Это то же самое, что и в случае с массивами - вы не можете передавать массивы в качестве аргументов функции, так что всякий раз, когда вы объявляете аргумент функции в виде массива, он молча превращается в указатель.

Ответ 2

В C и С++, если вы объявляете функциональный параметр, чтобы иметь тип функции, его тип будет скорректирован на указатель функции.

C99, §6.7.5.3/8

Объявление параметра как возвращаемого типа функции 'должно быть отрегулировано на' 'указатель на            возвращаемый тип функции, как в 6.3.2.1.

С++ 11, §8.3.5/5

... После определяя тип каждого параметра, любой параметр типа "массив T" или "возвращающая функцию T" является скорректированный на "указатель на T" или "указатель на функцию возврата T", соответственно...

Таким образом, в С++, например, мы можем писать типы как f1, так и f2 как void(void(*)()).

Ответ 3

Это альтернативный синтаксис для той же самой вещи. В обоих случаях передается указатель функции, но вы можете использовать синтаксис не указателя в списке параметров. Использование имени функции всегда распадается на указатель на функцию (и это может произойти рекурсивно).