С++ - вопросы о указателях функций

134
20

Я написал следующий код:


#include "stdafx.h"
#include <iostream>
using namespace std;

double funcA()
{
return 100.0;
}

int g(double (*pf)())
{
cout << (*pf)() << endl;
return 0;
}

int g2(double pf())
{
cout << pf() << endl;
return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
g(&funcA); // case I
g(funcA); // case II

g2(funcA); // case III
g2(&funcA); // case IV
return 0;
}


Я запустил вышеуказанный код на VS2008, и каждый вызов функции возвращает "100".
Вот вопрос:


Q1 > Есть ли какие-либо проблемы в коде?


Q2 > Кажется, что С++ не делает разницы между * pf и pf. Это правильно?


Спасибо

спросил(а) 2010-11-17T19:28:00+03:00 10 лет, 7 месяцев назад
1
Решение
134

С++ действительно делает различие между типами double() и double(*)(), но разница тонкая. Когда вы передаете тип функции в качестве аргумента функции, функция-тип автоматически "деградирует" на указатель функции. (Это похоже, я полагаю, на то, как тип массива ухудшается до типа указателя при передаче в качестве аргумента функции.)


Однако тип функции и тип указателя функции по-прежнему являются разными, в соответствии с системой типов С++. Рассмотрим следующий случай:


void g() { }

template <class F>
struct Foo
{
Foo(const F& f) : func(f)
{ }

void operator()() { func(); }

F func;
};

int main ()
{
Foo<void()> f(g);
f();
}

Это не должно компилироваться, поскольку вы не можете объявить тип функции как автоматическую переменную. (Помните, что функции не являются первоклассными объектами в С++.) Таким образом, объявление F func; является недопустимым. Однако, если мы изменим нашу функцию main, чтобы вместо этого создать шаблон с помощью указателя функции, например:


int main ()
{
typedef void(*function_pointer)();
Foo<function_pointer> f(g);
f();
}

... теперь он компилируется.

ответил(а) 2010-11-17T19:46:00+03:00 10 лет, 7 месяцев назад
78

Следующие функции идентичны:


int g(double (*pf)())
{
cout << (*pf)() << endl;
return 0;
}

int g2(double pf())
{
cout << pf() << endl;
return 0;
}


Вызов указателя функции (как показано в g) совпадает с вызовом этого имени функции.

Q2 > Кажется, что С++ не делает разница между * pf и pf. В том, что правильно?



Существует различие между * pf и pf (как переменными). Если pf - функция, то pf и pf() идентичны (обратите внимание на круглые скобки).

ответил(а) 2010-11-17T19:36:00+03:00 10 лет, 7 месяцев назад
45

Рассмотрим следующий фрагмент кода


void pf();

void (&prf)() = pf; // OK, bind prf to pf
void (&prf)() = &pf; // ill-formed, can't bind prf to an function pointer value


С другой стороны,


void (*ppf)() = pf; // OK, function decays to a pointer
void (*ppf)() = &pf; // OK, pointer assigned to a pointer

Таким образом, существует неявное преобразование из функции в указатель (который называется "распад" ). Это также позволяет вам сказать ***...***pf - произвольно много раз разыгрывать его - на каждом шаге происходит преобразование указателя, которое отменяет эффект предыдущего разыменования.


В списках параметров функции a T f() и a T (*f)() являются эквивалентными способами (кроме орфографии) объявления параметра


void f(void g()); // g has type void (*)()
void f(void (*g)()); // g has type void (*)()

Ссылка будет препятствовать настройке параметров этого параметра


void f(void (&g)()); // g has *not* the type void (*)()

Это точно так же, как для объявленных параметров массива: параметры никогда не являются массивами, но они всегда будут указателями, если они объявлены как массивы.

ответил(а) 2010-11-19T18:35:00+03:00 10 лет, 7 месяцев назад
45

В коде, который вы опубликовали, нет никаких проблем или различий. Однако, если вы пишете шаблоны, которые принимают функторы, вы должны использовать синтаксис в g2. Рассмотрим следующее:


template<typename Iter, typename Func>
void for_each(Iter begin, Iter end, Func functor)
{
for(; begin != end; ++begin)
{
functor(*begin);
}
}

Обратите внимание, что если вы поместите оператор разыменования до functor, вы ограничите утилиту алгоритма, который вы написали, указателям на функции. Однако, если вы этого не ставите, кто-то может передать функтор STL, например, что-то, возвращаемое std::bind2nd.


Поэтому я бы рекомендовал использовать второй (без *) синтаксис, где это возможно.

ответил(а) 2010-11-17T19:47:00+03:00 10 лет, 7 месяцев назад
45

С большинством современных компиляторов нет никакой разницы между "(* variable)". и "variable- > ". Тем не менее, нужно проверить используемый класс, чтобы убедиться, что он переопределяет оператор разыменования.


Многие программисты используют typedef при определении указателей функций, прежде всего для облегчения чтения. Кроме того, синтаксис double pf() может быть подвержен ошибкам чтения и может запутаться при выполнении функции в строке параметров.

ответил(а) 2010-11-17T19:37:00+03:00 10 лет, 7 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема