Установка указателя на нестационарную функцию-член
Я пытаюсь установить указатель функции, который устанавливается во время выполнения на основе набора пользовательских параметров. Я хотел бы, чтобы указатель функции указывал на нестационарную функцию-член, но я не могу найти, как это сделать.
Примеры, которые я видел, говорят, что это можно сделать только с помощью статической функции-члена или использовать глобальные переменные в прямой 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 );
}