Как назначить указатель члена метода подкласса?

Моя проблема немного сложная. У меня есть один класс (e: Component), в котором есть объекты портов. Когда компонент создает объект порта, он передает один из своих методов конструктору порта.

Сигнатуры методов:

typedef std::vector<std::string> (Component::*ComponentMethod)(std::vector<std::string>);

Он отлично работает, если я:

// in class Component

std::vector<std::string> Component::test_method( std::vector<std::string> ) {
    std::cout << "Hello !\n";
    // Do stuff
}

// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&Component::test_method);

Что ж ... теперь моя проблема в том, что я создаю подклассы класса Component, и я не знаю, как передавать методы подклассов в порт.

// in class SubtypeComponent

std::vector<std::string> SubtypeComponent::test_method2( std::vector<std::string> ) {
    std::cout << "Hello !\n";
    // Do stuff
}

// Port ctor : Port( ComponentMethod callback );
Port* p = new Port(&SubtypeComponent::test_method2);
// ERROR :'(

Это кажется нормальным: я думаю, компилятор ожидает именно Component (only) метод.

-> Я ищу решение для «динамического» назначения методов в портах (но я не думаю, что это возможно)

-> Может, другое решение - использование шаблонов? (определение «указателей методов шаблона» вместо «указателей методов компонентов»), но я не уверен.

Любая помощь будет оценена :)


person Neozaru    schedule 25.11.2012    source источник


Ответы (4)


Преобразования для указателя на член сначала интуитивно понятны. Указатель на член, который ссылается на базовый член, может быть неявно преобразован в указатель на член производного типа (противоположная дисперсия), но обратное неверно.

Если вы действительно думаете об этом, это имеет смысл: если у вас есть указатель на член base, он может быть применен к производному типу, поскольку он гарантированно имеет базовый подобъект. Напротив, указатель на член, который ссылается на производный тип, может указывать на член, которого нет в базовом типе, и поэтому преобразование не будет иметь смысла.

Это проблема в вашем дизайне, которую вы должны решить: если вы хотите сохранить указатели на член, относящиеся к базовому типу, указатели должны ссылаться на элементы, присутствующие в основе (в этом случае выражение &derived::member имеет тип T base::*, и преобразование не требуется), и они не должны быть ранее сохранены в указателе на член производного типа (после его сохранения компилятор не может знать, относится ли указатель к члену базового или член производного)

person David Rodríguez - dribeas    schedule 25.11.2012

Очевидно, вы не можете преобразовать указатель на член производного типа в указатель на член базового объекта: это позволило бы вызвать член производного типа в базовом объекте, не нарушая систему типов! Во всяком случае, преобразование должно работать наоборот, т. Е. Вы могли бы преобразовать указатель на член на основе указателя на член производного типа (однако я думаю это тоже невозможно, но я не уверен).

Способ полностью обойти эту проблему - использовать для передачи тип функции со стертым типом. Например, вы можете использовать std::function<std::vector<std::string>(std::vector<std::string>) и настроить его для ссылки на ваш объект. Если вам действительно нужно передать различные объекты в качестве аргумента, вы можете передать указатель или ссылку на базу и dynamic_cast<D&>(r) ее на ожидаемый тип (и обработать исключение, если оно когда-либо вызывается не на том объекте).

person Dietmar Kühl    schedule 25.11.2012

Вы можете использовать адаптер шаблона, поскольку указатели на члены могут быть преобразованы таким образом:

struct Base {
  bool foo(int);
};

struct Child : Base {
  bool foo(int);
};

// real functional type
using Function = std::function<bool(Base*,int)>;
// adapter template for children classes member functions
template <class T>
using MemberFunction = (T::*)(int);

// real user function
void user(Function function);
// adapter template for children classes member functions
template <class T>
void user(MemberFunction<T> function) {
  // pointers-to-members can be casted
  user(static_cast<MemberFunction<Base>>(function));
}
person Jim    schedule 03.09.2015

Самый простой способ сделать это, вероятно, будет, если вы позволите Component иметь виртуальный метод, называемый, например, test_method. Затем вы можете передать указатель на Component конструктору Port, а затем Port может просто вызвать этот виртуальный метод:

Component* c = new Component();
// Port ctor : Port( Component* component );
Port* p = new Port(c); // Port would then call c->test_method();

or

SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Component* component );
Port* p = new Port(sc); // Port would then call sc->test_method();

Однако, если по какой-то причине это не дает вам достаточной гибкости, есть другой способ решить вашу проблему: вам нужно передать тип вместо указателя метода. Затем этот тип содержит метод (и объект, потому что вы не можете вызвать метод без объекта). Вот пример этого типа (вы также можете использовать std :: function, если у вас есть C ++ 11):

class Handler {
    public:
    virtual std::vector<std::string> handle (std::vector<std::string> arguments) = 0;
};

template <class C> class Method: public Handler {
    C* object;
    std::vector<std::string> (C::*method) (std::vector<std::string>);
    public:
    Method (C *object, std::vector<std::string>(C::*method)(std::vector<std::string>)) {
        this->object = object;
        this->method = method;
    }
    virtual std::vector<std::string> handle (std::vector<std::string> arguments) {
        // call the method
        return (object->*method) (arguments);
    }
};

Теперь вы измените конструктор класса Port, чтобы он принимал указатель на обработчик в качестве аргумента. Тогда вы можете написать:

Component* c = new Component();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<Component>(c, &Component::test_method));

а также

SubtypeComponent* sc = new SubtypeComponent();
// Port ctor : Port( Handler* handler );
Port* p = new Port(new Method<SubtypeComponent>(sc, &SubtypeComponent::test_method2));

Также ознакомьтесь с этими вопросами:
Указатели на функции-члены C ++ в класс и подкласс
Приведение указателя метода

person eyelash    schedule 04.03.2013