Указатель на функцию-член на неопределенный тип класса - возможно ли это? - PullRequest
3 голосов
/ 17 января 2012

Можно ли объявить указатель на функцию (не на C ++ 11), который может указывать на функцию-член любого класса (читай: не определенный класс)?

Например, если бы у меня были классы A, B и C. В C объявлен указатель функции, и я бы хотел переключить этот указатель между указанием на одну из функций-членов B и одну из функций-членов A. C ++ позволяет это?

Ответы [ 5 ]

3 голосов
/ 17 января 2012

boost::function может сделать это вместе с boost::bind:

#incluce <boost/function.hpp>
#include <boost/bind.hpp>

struct A{ void f(); };
struct B{ void g(); };
struct C{
  boost::function<void()> _func;
};

int main(){
  A a; B b; C c;
  c.func = boost::bind(&A::f, &a);
  c.func();
  c.func = boost::bind(&B::g, &b);
  c.func();
}
2 голосов
/ 22 ноября 2015

Да, вы можете, но вы должны удалить некоторые типы безопасности и отслеживать указатель this , а также функцию-член.

Вы можете увидеть вариант использования в http://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates.. Основная идея - сохранить указатель объекта в виде пустого указателя и перенаправить вызов функции через статический метод.

#include <iostream>
using namespace std;

struct A { void foo() { cout << "A::foo\n"; } };
struct B { void foo() { cout << "B::foo\n"; } };

class C
{
public:
    C() : object_ptr(0), stub_ptr(0) {}

    template <class T, void (T::*TMethod)()>
    static C from_method(T* object_ptr)
    {
        C d;
        d.object_ptr = object_ptr;
        d.stub_ptr = &method_stub<T, TMethod>;
        return d;
    }

    void operator()() const
    {
        return (*stub_ptr)(object_ptr);
    }

private:
    typedef void (*stub_type)(void* object_ptr);

    void* object_ptr;
    stub_type stub_ptr;

    template <class T, void (T::*TMethod)()>
    static void method_stub(void* object_ptr)
    {
        T* p = static_cast<T*>(object_ptr);
        return (p->*TMethod)();
    }
};

int main() 
{
    A a;
    B b;

    C c = C::from_method<A, &A::foo>(&a);

    c();

    c = C::from_method<B, &B::foo>(&b);

    c();

    return 0;
}

Код выше должен напечатать

A::foo
B::foo

Это решение быстрее, чем использование std::function, поскольку оно не требует выделенного для кучи хранилища данных, но его можно использовать только для ссылки на функции-члены (в отличие от std::function, который претендует на владение любым вызываемым объектом).

1 голос
/ 17 января 2012

Это невозможно.Когда вы объявляете функцию-член класса, эта функция имеет неявный параметр this, поэтому даже если вы пишете void A::func(int i), функция на самом деле имеет такую ​​сигнатуру:

void func(A *const this, int i)

Вы собираетесьнужен что-то вроде Boost.Function и Boost.Bind, которые используются вместе, чтобы сделать то, что вы хотите достичь:

boost::function<void (int)> func;
A* a = new A;
func = boost::bind(&A::func, a, _1);
0 голосов
/ 17 января 2012

Нет, вы не можете.

И это не имело бы смысла, если бы вы могли.Представьте на мгновение, что вы могли бы, и у вас есть объект типа A, и вы вызываете указатель на функцию-член, который указывает на функцию-член типа B. Внезапно в функции-члене B вы получите указатель this, указывающий на адресобъекта A, но с типом B*.

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

Другой, более широкой альтернативой может быть использование boost::function объектов, которыеуже привязаны к объекту, для которого вы хотите вызвать функцию-член.

0 голосов
/ 17 января 2012

Нет, но вы можете сделать так, чтобы A и B имели общий базовый класс X, а затем вы могли бы использовать T (X::*)(U...) как для A, так и для B (при условии, что T и U одинаковы).

Или вы можете просто использовать boost::variant.

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