2015-08-25 5 views
1

Я рассмотрел верхние ответы C++ на шаблон посетителя и указатели на функции-члены; но я все еще не могу понять, как должен быть разработан следующий (довольно простой) сценарий.Функции указателей функций C++

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

class Optimizer{ 
    public: 
     typedef double (*Func1d)(double); 
     typedef double (*Func2d)(double,double); 
     void Optimize1d(Func1d* f1Ptr, double& x); 
     void Optimize2d(Func2d* f2Ptr, double& x, double& y); 
}; 

и два пример класса:

class A { 
    double A1d(double x){return x;} 
}; 

class B{ 
    double B2d(double x, double y){return x+y;} 
}; 

и основная функция, которую я хотел бы иметь возможность использовать следующим образом:

void main() 
{ 
    Optimizer opt; 
    double xA_opt, xB_opt, yB_opt; 
    opt.Optimize1d(&A::A1d,xA_opt); 
    opt.Optimize2d(&B::B2d, xB_opt, yB_opt); 
} 

Но до сих пор, Я не могу заставить его работать. Я не хочу, чтобы оптимизатор напрямую держал указатели на объекты типа A и B; потому что тогда ему нужно быть знакомым с этими объектами.

Надеюсь, этот вопрос имеет смысл. Благодаря!

+1

Зачем нужны указатели, а не виртуальные функции и интерфейс? –

+0

Я не «женат», чтобы использовать указатели. вот почему я думаю, что это вопрос дизайна, а не вариант реализации. Возможно, я должен снова пометить. Я был бы признателен за предложения! – zuuz

+1

Могут ли эти функции быть статическими (A :: A1d, B :: B2d)? –

ответ

2

Проблема заключается в том, что typedef double (*Func1d)(double); не является указателем-функцией-членом, а просто простым указателем-указателем.

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

Если вы не можете сделать A1d и B2d статические другие варианты, чтобы сделать ваши функции шаблона Optimize1d и Optimize2d принимает шаблонный функтор:

template<typename F> 
void Optimize1d(F f1, double& x); 
template<typename F> 
void Optimize2d(F f2, double& x, double& y); 

или std::function:

void Optimize1d(std::function<double(double)> f1, double& x); 
void Optimize2d(std::function<double(double, double)> f2, double& x, double& y); 

Оба можно вызвать с помощью лямбда-захвата экземпляра A или B:

A a; 
B b; 
opt.Optimize1d([&a](double x){return a.A1d(x); }, xA_opt); 
opt.Optimize2d([&b](double x, double y){return b.B2d(x, y); }, xB_opt, yB_opt); 

Edit: Если у вас нет C++ 11 вы можете определить свой собственный функтор, используя класс, который определяет operator() вместо лямбда. Класс будет хранить указатель или ссылку на экземпляр A или B в переменной-члена:

struct A1d { 
    A* a; 
    A1d(A& a) : a(&a) {} 
    double operator()(double x) { return a->A1d(x); } 
}; 

Вы можете построить экземпляр этого класса и передать его в шаблонных оптимизировать функции:

A1d a1d(a); 
opt.Optimize1d(a1d,xA_opt); 

Live demo.

, возможно, вы могли бы даже сделать ваши A или B классы функторов себя путем добавления operator() функции к ним?

+0

Есть ли способ конвертировать 'double a.A1d (double)' (функция-член определенного экземпляра) в 'std :: function < double (double)> 'без использования лямбда для захвата экземпляра? – user3528438

+2

@ user3528438 вы можете просто использовать std :: bind. но вам нужно иметь какой-то «захват», где (ссылка) объект типа A сохраняется в объекте-указателе функции. разница между лямбда-захватом и std :: bind в этом отношении невелика. –

+0

@ Крис Дрю: Спасибо за ваш ответ. Не могли бы вы рассказать о шаблоном решении для решения, отличного от C++ 11? – zuuz

2

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

Это ЬурейеЕ

typedef double (*Func1d)(double); 

является законным и в C и C++ код, и вы можете использовать C-стиль «свободные» функции этого типа.

Но на этой линии в главной функции:

opt.Optimize1d(&A::A1d,xA_opt); 

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

Проще всего включить заголовок <functional> и использовать для этого std::function<double(double)>. (Предположим, у вас есть C++ 11, иначе вы могли бы использовать boost::function.)

Есть и другие вещи, которые вы могли бы сделать, например, использовать виртуальную отправку участников, как это предлагается в комментариях. (IMO, который немного менее изящный.)

Вы также можете сделать функции optimize функциями шаблона и принять объект шаблонного типа и т. Д. И т. Д. Но на самом деле std::function - это путь сюда, он будет обрабатывать все эти детали для вас.