C ++ - Вопросы о функциональных указателях - PullRequest
8 голосов
/ 17 ноября 2010

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

#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> Кажется, что C ++ не делает различий между * pf и pf.Это правильно?

Спасибо

Ответы [ 5 ]

8 голосов
/ 17 ноября 2010

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

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

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();
}

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

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

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

2 голосов
/ 17 ноября 2010

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

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

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

Разыменование указателя на функцию (как показано на g) аналогично вызову имени этой функции.

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

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

0 голосов
/ 19 ноября 2010

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

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 (*)()

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

0 голосов
/ 17 ноября 2010

Нет проблем или различий в коде, который вы разместили. Однако, если вы пишете шаблоны, которые принимают функторы, вы должны использовать синтаксис в 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.

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

0 голосов
/ 17 ноября 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...