Установка указателя на нестационарную функцию-член

Я пытаюсь установить указатель функции, который устанавливается во время выполнения на основе набора пользовательских параметров. Я хотел бы, чтобы указатель функции указывал на нестационарную функцию-член, но я не могу найти, как это сделать.

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

Ниже приведен упрощенный пример:

    class CA
    {
    public:
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(double f(const double), double x) {
            char cTemp[256];
            sprintf_s(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
    };

Часть реализации

CA cA;
cA.setA(1.0);
cA.setB(2.0);

double (*p)(const double);

if(true) {
    p = &cA.getA;  //'&' : illegal operation on bound member function expression
} else {
    p = cA.getB;  //'CA::getB': function call missing argument list; use '&CA::getB' to create a pointer to member
                  //'=' : cannot convert from 'double (__thiscall CA::* )(const double)' to 'double (__cdecl *)(const double)'
}

cA.print(p, 3.0);

Итак, как мне получить p, чтобы указать на "getA" или "getB", чтобы он по-прежнему использовался "печатью".

Из того, что я видел, рекомендуется использовать boost или std:: bind, но у меня не было опыта ни с одним из них. Я надеюсь, что мне не нужно погружаться в них и что я просто что-то пропустил.

Компилятор MSVС++ 2008

Ответ 1

Не забывайте, что функция-член принимает неявный параметр this: поэтому функция-член, принимающая double, не может быть тем же самым, что и функция без члена (бесплатно), принимающая double.

// OK for global functions
double (*p)(const double);

// OK for member functions
double (CA:*p)(const double);

Также способ, которым вы их вызываете, отличается. Прежде всего, с функциями-членами вам нужен объект для их вызова (его адрес в конечном итоге будет привязан к указателю this в вызове функции). Во-вторых, вам нужно использовать оператор .* (или оператор ->*, если вы выполняете вызов через указатель):

p = &CA::getA;
CA cA;
(cA.*p)();

Последовательно вам нужно будет изменить свое определение функции print():

    #include <iostream>

    void print(double (CA::*f)(const double), double x) 
    {
        // Rather use the C++ I/O Library if you can...
        std::cout << "Value = " << (this->*f)(x);
    };

Итак, вот как вы должны переписать свою функцию main():

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    double (CA::*p)(const double);

    if (true) // Maybe use some more exciting condition :-)
    {
        p = &CA::getA;
    } 
    else {
        p = &CA::getB;
    }

    cA.print(p, 3.0);
}

Ответ 2

Ошибка компиляции

Этот ответ посвящен проблеме компиляции, представленной в вопросе. Я бы не рекомендовал использовать это как решение.

Указатели на функции-члены лучше всего обрабатывать с помощью typedef и макроса.

Здесь макрос для вызова функции-члена:

#define CALL_MEMBER_FN(object, ptrToMember)  ((object).*(ptrToMember))

Источник: [33.6] Как я могу избежать синтаксических ошибок при вызове функции-члена с помощью функции-указателя на член?, Часто задаваемые вопросы по С++.

Это избавит вас от необходимости запоминать уродливый синтаксис (object).*(ptrToMember) в любое время, когда вы хотите вызвать функцию-член указателем.

В class объявите typedef с именем CAGetter, это упростит объявление переменной:

class CA
{
public:    
    typedef double (CA::*CAGetter)(const double x);

Затем вы можете просто объявить свою функцию print():

    void print(CAGetter f, double x)

Тело также прост, ясен и краток:

    {
        std::cout << "value = " << CALL_MEMBER_FN(*this, f)(x) << '\n';
    }

Использование образца:

CA a;
a.setA(3.1);
a.setB(4.2);

// Using a variable...
CA::CAGetter p = &CA::getA;
a.print(p, 1);

// without a variable
a.print(&CA::getB, 1);

// Calling the functions from outside the class...
std::cout << "From outside (A): " << CALL_MEMBER_FN(a, p)(10) << std::endl;
std::cout << "From outside (B): " << CALL_MEMBER_FN(a, &CA::getB)(10) << std::endl;

Проблема дизайна

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

Ответ 3

Вы можете использовать указатель на метод:

class CA
{
    public:
        typedef double (CA::*getter)( double );
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", (this->*f)(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = &CA::getA;  
    } else {
            p = &CA::getB; 
    cA.print( p, 3.0 );
}

Или используйте boost:: bind

class CA
{
    public:
        typedef boost::function<double( double )> getter;
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = boost::bind( &CA::getA, &cA, _1 );
    } else {
            p = boost::bind( &CA::getB, &cA, _1 );
    }
    cA.print( p, 3.0 );
}