Покажчик функції на функцію-член


89

Я хотів би встановити вказівник на функцію як член класу, який є вказівником на іншу функцію в тому ж класі. Причини, чому я це роблю, складні.

У цьому прикладі я хотів би, щоб результат був "1"

class A {
public:
 int f();
 int (*x)();
}

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = a.f;
 printf("%d\n",a.x())
}

Але це не вдається скласти. Чому?



@jww і перевірте відповідь CiroSantilli у цьому питанні, інші відповіді більш-менш не відповідають темі. В основному, просто int (C :: * function_pointer_var) (int) = & C :: method; тоді C c; та (c. * function_pointer_var) (2).
jw_

Відповіді:


157

Синтаксис неправильний. Покажчик члена - це категорія, що відрізняється від звичайного вказівника. Вказівник на член повинен бути використаний разом з об'єктом свого класу:

class A {
public:
 int f();
 int (A::*x)(); // <- declare by saying what class it is a pointer to
};

int A::f() {
 return 1;
}


int main() {
 A a;
 a.x = &A::f; // use the :: syntax
 printf("%d\n",(a.*(a.x))()); // use together with an object of its class
}

a.xще не говорить про те, на якому об’єкті слід викликати функцію. Це просто говорить, що ви хочете використовувати покажчик, що зберігається в об’єкті a. Передбачаючи aінший раз, як лівий операнд .*оператору, він повідомить компілятору, на якому об'єкті потрібно викликати функцію.


Я знаю, що це старе, але я не розумію використання (a.*a.x)().Чому (a.*x)()не працює?
Gaurav Sehgal

3
@gau, тому що х не входить до сфери дії
Йоханнес Шауб - літб

13
Мені доводиться шукати це кожного разу, коли я його також використовую. Синтаксис заплутаний, але це має сенс, якщо його розбити. a.x- це вказівник на функцію-член класу А., що *a.xпосилається на вказівник, тому тепер це посилання на функцію. a.(*a.x)"прив'язує" функцію до екземпляра (так само, як a.f). (a.(*a.x))необхідно згрупувати цей складний синтаксис і (a.(*a.x))()фактично викликає метод aбез аргументів.
jwm

23

int (*x)()не є покажчиком на функцію-член. Покажчик на функцію - член записується в такий спосіб: int (A::*x)(void) = &A::f;.


17

Виклик функції члена за командою рядка

#include <iostream>
#include <string>


class A 
{
public: 
    void call();
private:
    void printH();
    void command(std::string a, std::string b, void (A::*func)());
};

void A::printH()
{
    std::cout<< "H\n";
}

void A::call()
{
    command("a","a", &A::printH);
}

void A::command(std::string a, std::string b, void (A::*func)())
{
    if(a == b)
    {
        (this->*func)();
    }
}

int main()
{
    A a;
    a.call();
    return 0;
}

Зверніть увагу на (this->*func)();спосіб оголошення вказівника на функцію з назвою класуvoid (A::*func)()


11

Вам потрібно використовувати вказівник на функцію-член, а не просто вказівник на функцію.

class A { 
    int f() { return 1; }
public:
    int (A::*x)();

    A() : x(&A::f) {}
};

int main() { 
   A a;
   std::cout << (a.*a.x)();
   return 0;
}

3

Хоча це базується на стерлінг-відповідях в інших місцях на цій сторінці, у мене був випадок використання, який не був повністю вирішений ними; для вектора покажчиків на функції виконайте наступне:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>

class A{
public:
  typedef vector<int> (A::*AFunc)(int I1,int I2);
  vector<AFunc> FuncList;
  inline int Subtract(int I1,int I2){return I1-I2;};
  inline int Add(int I1,int I2){return I1+I2;};
  ...
  void Populate();
  void ExecuteAll();
};

void A::Populate(){
    FuncList.push_back(&A::Subtract);
    FuncList.push_back(&A::Add);
    ...
}

void A::ExecuteAll(){
  int In1=1,In2=2,Out=0;
  for(size_t FuncId=0;FuncId<FuncList.size();FuncId++){
    Out=(this->*FuncList[FuncId])(In1,In2);
    printf("Function %ld output %d\n",FuncId,Out);
  }
}

int main(){
  A Demo;
  Demo.Populate();
  Demo.ExecuteAll();
  return 0;
}

Щось подібне корисно, якщо ви пишете інтерпретатор команд з індексованими функціями, які потрібно поєднати з синтаксисом параметрів та підказками тощо. Можливо, це також корисно в меню.


1
Як визначено, AFunc - це вказівник на функцію-член, яка бере два ints і повертає вектор ints. Але члени вказали на повернення int, так? Я думаю, що вислів typedef повинен бути typedef int (A::*AFunc)(int I1,int I2);
riderBill

2

Хоча ви, на жаль, не можете перетворити існуючий покажчик функції члена на звичайний вказівник функції, ви можете створити шаблон функції адаптера досить простим способом, який оберне вказівник функції члена, відомий під час компіляції, у звичайну функцію, як це:

template <class Type>
struct member_function;

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...)>
{
    template <Ret(Type::*Func)(Args...)>
    static Ret adapter(Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

template <class Type, class Ret, class... Args>
struct member_function<Ret(Type::*)(Args...) const>
{
    template <Ret(Type::*Func)(Args...) const>
    static Ret adapter(const Type &obj, Args&&... args)
    {
        return (obj.*Func)(std::forward<Args>(args)...);
    }
};

 

int (*func)(A&) = &member_function<decltype(&A::f)>::adapter<&A::f>;

Зверніть увагу, що для виклику функції-члена Aповинен бути наданий екземпляр .


Ви надихнули мене, @ IllidanS4. Дивіться мою відповідь. +1
memtha

1

Спираючись на відповідь @ IllidanS4, я створив клас шаблону, який дозволяє практично будь-яку функцію-член із заздалегідь визначеними аргументами та екземпляр класу передавати за посиланням для подальшого виклику.



template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs&&... rargs) = 0;
    //virtual RET call() = 0;
};

template<class T, class RET, class... RArgs> class CallbackCalltimeArgs : public Callback_t<RET, RArgs...> {
public:
    T * owner;
    RET(T::*x)(RArgs...);
    RET call(RArgs&&... rargs) {
        return (*owner.*(x))(std::forward<RArgs>(rargs)...);
    };
    CallbackCalltimeArgs(T* t, RET(T::*x)(RArgs...)) : owner(t), x(x) {}
};

template<class T, class RET, class... Args> class CallbackCreattimeArgs : public Callback_t<RET> {
public:
    T* owner;
    RET(T::*x)(Args...);
    RET call() {
        return (*owner.*(x))(std::get<Args&&>(args)...);
    };
    std::tuple<Args&&...> args;
    CallbackCreattimeArgs(T* t, RET(T::*x)(Args...), Args&&... args) : owner(t), x(x),
        args(std::tuple<Args&&...>(std::forward<Args>(args)...)) {}
};

Тест / приклад:

class container {
public:
    static void printFrom(container* c) { c->print(); };
    container(int data) : data(data) {};
    ~container() {};
    void print() { printf("%d\n", data); };
    void printTo(FILE* f) { fprintf(f, "%d\n", data); };
    void printWith(int arg) { printf("%d:%d\n", data, arg); };
private:
    int data;
};

int main() {
    container c1(1), c2(20);
    CallbackCreattimeArgs<container, void> f1(&c1, &container::print);
    Callback_t<void>* fp1 = &f1;
    fp1->call();//1
    CallbackCreattimeArgs<container, void, FILE*> f2(&c2, &container::printTo, stdout);
    Callback_t<void>* fp2 = &f2;
    fp2->call();//20
    CallbackCalltimeArgs<container, void, int> f3(&c2, &container::printWith);
    Callback_t<void, int>* fp3 = &f3;
    fp3->call(15);//20:15
}

Очевидно, це буде працювати лише в тому випадку, якщо наведені аргументи та клас власника все ще є дійсними. Що стосується читабельності ... пробачте, будь ласка.

Редагувати: видалено непотрібний malloc, зробивши кортеж нормальним сховищем. Додано успадкований тип для посилання. Додана можливість надати всі аргументи під час виклику. Зараз працюємо над тим, щоб мати обидва ....

Редагування 2: Як і обіцяли, обидва. Єдиним обмеженням (яке я бачу) є те, що заздалегідь визначені аргументи повинні надходити перед аргументами, що надаються в процесі виконання у функції зворотного виклику. Дякуємо @Chipster за допомогу щодо дотримання вимог GCC. Це працює на gcc на ubuntu та visual studio на windows.

#ifdef _WIN32
#define wintypename typename
#else
#define wintypename
#endif

template<class RET, class... RArgs> class Callback_t {
public:
    virtual RET call(RArgs... rargs) = 0;
    virtual ~Callback_t() = default;
};

template<class RET, class... RArgs> class CallbackFactory {
private:
    template<class T, class... CArgs> class Callback : public Callback_t<RET, RArgs...> {
    private:
        T * owner;
        RET(T::*x)(CArgs..., RArgs...);
        std::tuple<CArgs...> cargs;
        RET call(RArgs... rargs) {
            return (*owner.*(x))(std::get<CArgs>(cargs)..., rargs...);
        };
    public:
        Callback(T* t, RET(T::*x)(CArgs..., RArgs...), CArgs... pda);
        ~Callback() {};
    };
public:
    template<class U, class... CArgs> static Callback_t<RET, RArgs...>* make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...));
};
template<class RET2, class... RArgs2> template<class T2, class... CArgs2> CallbackFactory<RET2, RArgs2...>::Callback<T2, CArgs2...>::Callback(T2* t, RET2(T2::*x)(CArgs2..., RArgs2...), CArgs2... pda) : x(x), owner(t), cargs(std::forward<CArgs2>(pda)...) {}
template<class RET, class... RArgs> template<class U, class... CArgs> Callback_t<RET, RArgs...>* CallbackFactory<RET, RArgs...>::make(U* owner, CArgs... cargs, RET(U::*func)(CArgs..., RArgs...)) {
    return new wintypename CallbackFactory<RET, RArgs...>::Callback<U, CArgs...>(owner, func, std::forward<CArgs>(cargs)...);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.